diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..900036b7c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ + +.vscode +*.code-workspace +pkg/* +build +gopkgs +__pycache__ +*.pyc +*.rdb +*.swp + +*.yin +*.tree +src/translib/ocbinds/ocbinds.go + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..374783922c --- /dev/null +++ b/Makefile @@ -0,0 +1,204 @@ +################################################################################ +# # +# 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 clitree + +TOPDIR := $(abspath .) +BUILD_DIR := $(TOPDIR)/build +export TOPDIR + +ifeq ($(BUILD_GOPATH),) +export BUILD_GOPATH=$(TOPDIR)/gopkgs +endif + +export GOPATH=$(BUILD_GOPATH):$(TOPDIR) + +ifeq ($(GO),) +GO := /usr/local/go/bin/go +export GO +endif + +INSTALL := /usr/bin/install + +MAIN_TARGET = sonic-mgmt-framework_1.0-01_amd64.deb + +GO_DEPS_LIST = github.com/gorilla/mux \ + github.com/Workiva/go-datastructures/queue \ + github.com/openconfig/goyang \ + github.com/openconfig/ygot/ygot \ + github.com/go-redis/redis \ + github.com/golang/glog \ + github.com/pkg/profile \ + gopkg.in/go-playground/validator.v9 \ + golang.org/x/crypto/ssh \ + github.com/antchfx/jsonquery \ + github.com/antchfx/xmlquery \ + github.com/facette/natsort \ + github.com/philopon/go-toposort \ + gopkg.in/godbus/dbus.v5 \ + github.com/dgrijalva/jwt-go + + +REST_BIN = $(BUILD_DIR)/rest_server/main +CERTGEN_BIN = $(BUILD_DIR)/rest_server/generate_cert + + +all: build-deps go-deps go-redis-patch go-patch translib rest-server cli + +build-deps: + mkdir -p $(BUILD_DIR) + +go-deps: $(GO_DEPS_LIST) + +go-redis-patch: go-deps + cd $(BUILD_GOPATH)/src/github.com/go-redis/redis; git checkout d19aba07b47683ef19378c4a4d43959672b7cec8 2>/dev/null ; true; \ +$(GO) install -v -gcflags "-N -l" $(BUILD_GOPATH)/src/github.com/go-redis/redis + +$(GO_DEPS_LIST): + $(GO) get -v $@ + +cli: + $(MAKE) -C src/CLI + +clitree: + TGT_DIR=$(BUILD_DIR)/cli $(MAKE) -C src/CLI/clitree + +cvl: go-deps go-patch go-redis-patch + $(MAKE) -C src/cvl + $(MAKE) -C src/cvl/schema + $(MAKE) -C src/cvl/testdata/schema + +cvl-test: + $(MAKE) -C src/cvl gotest + +rest-server: translib + $(MAKE) -C src/rest + +rest-clean: + $(MAKE) -C src/rest clean + +translib: cvl + $(MAKE) -C src/translib + +codegen: + $(MAKE) -C models + +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; \ +cd ../; cp $(TOPDIR)/ygot-modified-files/ygot.patch .; \ +patch -p1 < ygot.patch; rm -f ygot.patch; \ +$(GO) install -v -gcflags "-N -l" $(BUILD_GOPATH)/src/github.com/openconfig/ygot/ygot; \ + cd $(BUILD_GOPATH)/src/github.com/openconfig/goyang/; git reset --hard HEAD; git checkout 064f9690516f4f72db189f4690b84622c13b7296 >/dev/null ; true; \ +cp $(TOPDIR)/goyang-modified-files/annotate.go $(BUILD_GOPATH)/src/github.com/openconfig/goyang/annotate.go; \ +cp $(TOPDIR)/goyang-modified-files/goyang.patch .; \ +patch -p1 < goyang.patch; rm -f goyang.patch; \ +$(GO) install -v -gcflags "-N -l" $(BUILD_GOPATH)/src/github.com/openconfig/goyang + +#Apply CVL related patches + $(apply_cvl_dep_patches) + +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)/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/ + cp -rf $(TOPDIR)/src/cvl/conf/cvl_cfg.json $(DESTDIR)/usr/sbin/cvl_cfg.json + +# Copy all CVL schema files + $(install_cvl_schema) + + # Scripts for host service + $(INSTALL) -d $(DESTDIR)/usr/lib/sonic_host_service/host_modules + $(INSTALL) -D $(TOPDIR)/scripts/sonic_host_server.py $(DESTDIR)/usr/lib/sonic_host_service + $(INSTALL) -D $(TOPDIR)/scripts/host_modules/*.py $(DESTDIR)/usr/lib/sonic_host_service/host_modules + $(INSTALL) -d $(DESTDIR)/etc/dbus-1/system.d + $(INSTALL) -D $(TOPDIR)/scripts/org.sonic.hostservice.conf $(DESTDIR)/etc/dbus-1/system.d + $(INSTALL) -d $(DESTDIR)/lib/systemd/system + $(INSTALL) -D $(TOPDIR)/scripts/sonic-hostservice.service $(DESTDIR)/lib/systemd/system + + + +ifeq ($(SONIC_COVERAGE_ON),y) + echo "" > $(DESTDIR)/usr/sbin/.test +endif + +$(addprefix $(DEST)/, $(MAIN_TARGET)): $(DEST)/% : + mv $* $(DEST)/ + +clean: rest-clean + $(MAKE) -C src/cvl clean + $(MAKE) -C src/translib clean + $(MAKE) -C src/cvl/schema clean + $(MAKE) -C src/cvl cleanall + rm -rf build/* + rm -rf debian/.debhelper + rm -rf $(BUILD_GOPATH)/src/github.com/openconfig/goyang/annotate.go + +cleanall: + $(MAKE) -C src/cvl cleanall + rm -rf build/* + +#Function to apply CVL related patches +define apply_cvl_dep_patches + + cd $(BUILD_GOPATH)/src/github.com/antchfx/jsonquery; git reset --hard HEAD; \ + git checkout 3b69d31134d889b501e166a035a4d5ecb8c6c367; git apply $(TOPDIR)/patches/jsonquery.patch + cd $(BUILD_GOPATH)/src/github.com/antchfx/xmlquery; git reset --hard HEAD; \ + git checkout fe009d4cc63c3011f05e1dfa75a27899acccdf11; git apply $(TOPDIR)/patches/xmlquery.patch + +endef + +#Function to install CVL schema files +define install_cvl_schema + $(INSTALL) -d $(DESTDIR)/usr/sbin/schema/ + $(INSTALL) -D $(TOPDIR)/src/cvl/schema/*.yin $(DESTDIR)/usr/sbin/schema/ + $(INSTALL) -D $(TOPDIR)/src/cvl/testdata/schema/*.yin $(DESTDIR)/usr/sbin/schema/ + +#Find all platform directories first and create them in destination + if [ -d $(TOPDIR)/src/cvl/schema/platform ] ; then \ + for dd in $$(find $(TOPDIR)/src/cvl/schema/platform -type d) ; \ + do \ + dds=$$(echo $${dd} | sed 's/^.*\(schema\/.*\)/\1/g') ; \ + $(INSTALL) -d $(DESTDIR)/usr/sbin/$${dds} ; \ + done ; \ + fi +#For each platform directory, copy all platform YANG files to destination + if [ -d $(TOPDIR)/src/cvl/schema/platform ] ; then \ + for ff in $$(find $(TOPDIR)/src/cvl/schema/platform -name '*.yin') ; \ + do \ + ffs=$$(echo $${ff} | sed 's/^.*\(schema\/.*\)/\1/g') ; \ + $(INSTALL) -T $(TOPDIR)/src/cvl/$${ffs} $(DESTDIR)/usr/sbin/$${ffs} ; \ + done ; \ + fi +endef + diff --git a/README.md b/README.md new file mode 100644 index 0000000000..b33661e3d8 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +## SONiC Management Framework Repo + +### Build Instruction +Please note that the build instruction in this guide has only been tested on Ubuntu 16.04. +#### Pre-rerequisit +##### User permissions: + `sudo usermod -aG sudo $USER` + `sudo usermod -aG docker $USER` + +##### Packages to be installed: + `sudo apt-get install git docker` + +#### Steps to build and create an installer +1. git clone https://github.com/project-arlo/sonic-buildimage.git +2. cd sonic-buildimage/ +3. sudo modprobe overlay +4. make init +5. make configure PLATFORM=broadcom +6. Run the prefetch python script to download all binaries (see below for the script). +7. To build mgmt-framework container: + `BLDENV=stretch make target/docker-sonic-mgmt-framework.gz` +8. To build Debian Stretch, if not already downloaded: + `BLDENV=stretch make stretch` +9. To build the ONIE installer: + `BLDENV=stretch make target/sonic-broadcom.bin` + +#### Faster builds +In order to speed up the process of build, you can prefetch the latest debian files from Azure server, and just build what you need. + +Here is a python script you could use to fetch latest prebuilt objects (deb, gz, ko, etc) from SONiC Jenkins cluster: + + import os + import shutil + import urllib.request + from html.parser import HTMLParser + + UPSTREAM_PREFIX = 'https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/buildimage-brcm-all/lastSuccessfulBuild/artifact/' + + def get_all_bins(target_path, extension): + """Get all files matching the given extension from the target path""" + print('Fetching %s*%s' % (target_path, extension)) + os.makedirs(target_path, exist_ok=True) + + req = urllib.request.urlopen(UPSTREAM_PREFIX + target_path) + data = req.read().decode() + + class Downloader(HTMLParser): + """Class to parse retrieved data, match against the given extension, + and download the matching files to the given target directory""" + def handle_starttag(self, tag, attrs): + """Handle only tags""" + if tag == 'a': + for attr, val in attrs: + if attr == 'href' and val.endswith(extension): + self.download_file(val) + + @staticmethod + def download_file(path): + filename = os.path.join(target_path, path) + freq = urllib.request.urlopen(UPSTREAM_PREFIX + target_path + path) + + print('\t%s' % path) + with open(filename, 'wb') as fp: + shutil.copyfileobj(freq, fp) + + + parser = Downloader() + parser.feed(data) + print() + + get_all_bins('target/debs/stretch/', '.deb') + get_all_bins('target/files/stretch/', '.ko') + get_all_bins('target/python-debs/', '.deb') + get_all_bins('target/python-wheels/', '.whl') + get_all_bins('target/', '.gz') + + + +##### Incremental builds +Just clean up the deb's/gz that require re-build, and build again. Here is an exmple: + +##### To build deb file for sonic-mgmt-framework + + BLDENV=stretch make target/debs/stretch/sonic-mgmt-framework_1.0-01_amd64.deb-clean + BLDENV=stretch make target/debs/stretch/sonic-mgmt-framework_1.0-01_amd64.deb + +##### To build sonic-mgmt-framework docker alone + + BLDENV=stretch make target/docker-sonic-mgmt-framework.gz-clean + BLDENV=stretch make target/docker-sonic-mgmt-framework.gz diff --git a/config/transformer/models_list b/config/transformer/models_list new file mode 100644 index 0000000000..a884849bb0 --- /dev/null +++ b/config/transformer/models_list @@ -0,0 +1,14 @@ +#List yang models transformer need to load + +openconfig-acl.yang +openconfig-acl-annot.yang +openconfig-network-instance.yang +openconfig-network-instance-annot.yang +sonic-vxlan.yang +sonic-udld.yang +sonic-udld-annot.yang +ietf-ptp.yang +ietf-ptp-annot.yang +sonic-mclag.yang +openconfig-system.yang +openconfig-system-annot.yang diff --git a/debian/.gitignore b/debian/.gitignore new file mode 100644 index 0000000000..8dbce7bb0d --- /dev/null +++ b/debian/.gitignore @@ -0,0 +1,9 @@ +.debhelper/ +*.debhelper +*.debhelper.log +*.substvars +sonic-mgmt-framework/ +sonic-host-service/ +tmp/ +files + diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000000..91d0815d54 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +sonic-mgmt-framework (1.0-01) UNRELEASED; urgency=low + + * Initial release. + + -- Prabhu Sreenivasan Tue, 18 Jun 2019 00:25:19 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000000..ec635144f6 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +9 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000000..abe2dbee42 --- /dev/null +++ b/debian/control @@ -0,0 +1,18 @@ +Source: sonic-mgmt-framework +Maintainer: Prabhu Sreenivasan +Build-Depends: debhelper (>= 8.0.0), + dh-systemd +Standards-Version: 3.9.3 +Section: net + +Package: sonic-mgmt-framework +Priority: extra +Architecture: amd64 +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: SONiC Management Framework + +Package: sonic-host-service +Priority: extra +Architecture: amd64 +Depends: python-dbus, python-gobject, python-systemd, ${shlibs:Depends}, ${misc:Depends} +Description: SONiC Host Service diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000000..c08b68a370 --- /dev/null +++ b/debian/rules @@ -0,0 +1,7 @@ +#!/usr/bin/make -f +%: + dh $@ --with systemd + + +override_dh_shlibdeps: + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info -l$(shell pwd)/build/cli/target/.libs/:$(shell pwd)/build/cli/.libs/ diff --git a/debian/sonic-host-service.install b/debian/sonic-host-service.install new file mode 100644 index 0000000000..04e910b716 --- /dev/null +++ b/debian/sonic-host-service.install @@ -0,0 +1,3 @@ +usr/lib/* +etc/dbus-1/system.d/* +lib/systemd/system/* diff --git a/debian/sonic-mgmt-framework.install b/debian/sonic-mgmt-framework.install new file mode 100644 index 0000000000..7dfc787b80 --- /dev/null +++ b/debian/sonic-mgmt-framework.install @@ -0,0 +1,3 @@ +usr/sbin/* +rest_ui/* +usr/models/* 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/goyang.patch b/goyang-modified-files/goyang.patch new file mode 100644 index 0000000000..06844b5179 --- /dev/null +++ b/goyang-modified-files/goyang.patch @@ -0,0 +1,129 @@ +diff --git a/README.md b/README.md +index 4d22c1e..805adb5 100644 +--- a/README.md ++++ b/README.md +@@ -14,6 +14,7 @@ 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. +diff --git a/pkg/yang/entry.go b/pkg/yang/entry.go +index ef658d6..f626dc9 100644 +--- a/pkg/yang/entry.go ++++ b/pkg/yang/entry.go +@@ -80,6 +80,7 @@ type Entry struct { + + // 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 +@@ -115,6 +116,10 @@ type Entry struct { + // the augmenting entity per RFC6020 Section 7.15.2. The namespace + // of the Entry should be accessed using the Namespace function. + namespace *Value ++ ++ ChildSchemaCache map[reflect.StructTag]*Entry `json:"-"` ++ ++ IsSchemaValidated bool `json:"-"` + } + + // An RPCEntry contains information related to an RPC Node. +@@ -264,6 +269,7 @@ 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{}{}, +@@ -366,6 +372,7 @@ func (e *Entry) add(key string, value *Entry) *Entry { + return e + } + e.Dir[key] = value ++ e.DirOKeys = append(e.DirOKeys, key) + return e + } + +@@ -1090,6 +1097,7 @@ func (e *Entry) FixChoice() { + } + ce.Parent = ne + e.Dir[k] = ne ++ e.DirOKeys = append(e.DirOKeys, k) + } + } + } +@@ -1260,6 +1268,14 @@ func (e *Entry) shallowDup() *Entry { + // 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 { +@@ -1283,6 +1299,14 @@ func (e *Entry) dup() *Entry { + // 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 { +@@ -1317,6 +1341,7 @@ func (e *Entry) merge(prefix *Value, namespace *Value, oe *Entry) { + } else { + v.Parent = e + e.Dir[k] = v ++ e.DirOKeys = append(e.DirOKeys, k) + } + } + } +@@ -1378,8 +1403,8 @@ func (s sortedErrors) Less(i, j int) bool { + } + return nless(fi[x], fj[x]) + } +- for x := 1; x < 4; x++ { +- switch compare(1) { ++ for x := 0; x < len(fi) && x < len(fj); x++ { ++ switch compare(x) { + case -1: + return true + case 1: +diff --git a/yang.go b/yang.go +index 2480a4e..515d1b3 100644 +--- a/yang.go ++++ b/yang.go +@@ -58,6 +58,7 @@ import ( + type formatter struct { + name string + f func(io.Writer, []*yang.Entry) ++ utilf func([]string, map[string]*yang.Module) + help string + flags *getopt.Set + } +@@ -208,5 +209,8 @@ Formats: + entries[x] = yang.ToEntry(mods[n]) + } + ++ if format == "annotate" { ++ formatters[format].utilf(files, mods) ++ } + formatters[format].f(os.Stdout, entries) + } diff --git a/models/.gitkeep b/models/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/models/Makefile b/models/Makefile new file mode 100644 index 0000000000..f180d3e7da --- /dev/null +++ b/models/Makefile @@ -0,0 +1,170 @@ +################################################################################ +# # +# 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)) + +BUILD_DIR := $(TOPDIR)/build +CODEGEN_TOOLS_DIR := $(TOPDIR)/tools/swagger_codegen + +CODEGEN_VER := 2.4.5 +CODEGEN_JAR := $(CODEGEN_TOOLS_DIR)/swagger-codegen-cli-$(CODEGEN_VER).jar + +SERVER_BUILD_DIR := $(BUILD_DIR)/rest_server +SERVER_CODEGEN_DIR := $(SERVER_BUILD_DIR)/codegen +SERVER_DIST_DIR := $(SERVER_BUILD_DIR)/dist +SERVER_DIST_INIT := $(SERVER_DIST_DIR)/.init_done +SERVER_DIST_GO := $(SERVER_DIST_DIR)/src/swagger +SERVER_DIST_UI := $(SERVER_DIST_DIR)/ui +SERVER_DIST_UI_HOME := $(SERVER_DIST_DIR)/ui/index.html + +include codegen.config + +YANGAPI_DIR := $(TOPDIR)/build/yaml +YANGAPI_SPECS := $(shell find $(YANGAPI_DIR) -name '*.yaml') +YANGAPI_NAMES := $(filter-out $(YANGAPI_EXCLUDES), $(basename $(notdir $(YANGAPI_SPECS)))) +YANGAPI_SERVERS := $(addsuffix /.yangapi_done, $(addprefix $(SERVER_CODEGEN_DIR)/, $(YANGAPI_NAMES))) + +OPENAPI_DIR := openapi +OPENAPI_SPECS := $(shell find $(OPENAPI_DIR) -name '*.yaml') +OPENAPI_NAMES := $(filter-out $(OPENAPI_EXCLUDES), $(basename $(notdir $(OPENAPI_SPECS)))) +OPENAPI_SERVERS := $(addsuffix /.openapi_done, $(addprefix $(SERVER_CODEGEN_DIR)/, $(OPENAPI_NAMES))) + +PY_YANGAPI_NAMES := $(filter $(YANGAPI_NAMES), $(PY_YANGAPI_CLIENTS)) +PY_OPENAPI_NAMES := $(filter $(OPENAPI_NAMES), $(PY_OPENAPI_CLIENTS)) +PY_CLIENT_CODEGEN_DIR := $(BUILD_DIR)/swagger_client_py +PY_CLIENT_TARGETS := $(addsuffix .yangapi_client, $(addprefix $(PY_CLIENT_CODEGEN_DIR)/, $(PY_YANGAPI_NAMES))) \ + $(addsuffix .openapi_client, $(addprefix $(PY_CLIENT_CODEGEN_DIR)/, $(PY_OPENAPI_NAMES))) + +UIGEN_DIR = $(TOPDIR)/tools/ui_gen +UIGEN_SRCS = $(shell find $(UIGEN_DIR) -type f) + + +.PHONY: all clean cleanall go-server py-client + +all: go-server py-client + +go-server: $(YANGAPI_SERVERS) $(OPENAPI_SERVERS) $(SERVER_DIST_INIT) $(SERVER_DIST_UI_HOME) + +py-client: $(PY_CLIENT_CODEGEN_DIR)/. $(PY_CLIENT_TARGETS) + +$(SERVER_DIST_UI_HOME): $(YANGAPI_SERVERS) $(OPENAPI_SERVERS) $(UIGEN_SRCS) + @echo "+++ Generating landing page for Swagger UI +++" + $(UIGEN_DIR)/src/uigen.py + + +.SECONDEXPANSION: + +#====================================================================== +# Common rule for directories. Use "." suffix, like "xyz/." +#====================================================================== +.PRECIOUS: %/. +%/.: + mkdir -p $@ + +#====================================================================== +# Download swagger codegen jar from Maven.org repo. It will be saved as +# build/swagger-codegen-cli.jar file. +#====================================================================== +$(CODEGEN_JAR): | $$(@D)/. + cd $(@D) && \ + wget http://central.maven.org/maven2/io/swagger/swagger-codegen-cli/$(CODEGEN_VER)/$(@F) + +#====================================================================== +# Generate swagger server in GO language for Yang generated OpenAPIs +# specs. +#====================================================================== +%/.yangapi_done: $(YANGAPI_DIR)/$$(*F).yaml | $$(@D)/. $(CODEGEN_JAR) $(SERVER_DIST_INIT) + @echo "+++ Generating GO server for Yang API $$(basename $(@D)).yaml +++" + java -jar $(CODEGEN_JAR) generate \ + --lang go-server \ + --input-spec $(YANGAPI_DIR)/$$(basename $(@D)).yaml \ + --template-dir $(CODEGEN_TOOLS_DIR)/go-server/templates-yang \ + --output $(@D) + cp $(@D)/go/api_* $(SERVER_DIST_GO)/ + cp $(@D)/go/routers.go $(SERVER_DIST_GO)/routers_$$(basename $(@D)).go + cp $(@D)/api/swagger.yaml $(SERVER_DIST_UI)/$$(basename $(@D)).yaml + touch $@ + +#====================================================================== +# Generate swagger server in GO language for handcoded OpenAPI specs +#====================================================================== +%/.openapi_done: $(OPENAPI_DIR)/$$(*F).yaml | $$(@D)/. $(CODEGEN_JAR) $(SERVER_DIST_INIT) + @echo "+++ Generating GO server for OpenAPI $$(basename $(@D)).yaml +++" + java -jar $(CODEGEN_JAR) generate \ + --lang go-server \ + --input-spec $(OPENAPI_DIR)/$$(basename $(@D)).yaml \ + --template-dir $(CODEGEN_TOOLS_DIR)/go-server/templates-nonyang \ + --output $(@D) + cp $(@D)/go/api_* $(@D)/go/model_* $(SERVER_DIST_GO)/ + cp $(@D)/go/routers.go $(SERVER_DIST_GO)/routers_$$(basename $(@D)).go + cp $(@D)/api/swagger.yaml $(SERVER_DIST_UI)/$$(basename $(@D)).yaml + touch $@ + +#====================================================================== +# Initialize dist directory for GO server code +#====================================================================== +$(SERVER_DIST_INIT): | $$(@D)/. + cp -r $(CODEGEN_TOOLS_DIR)/ui-dist $(@D)/ui + cp -r $(CODEGEN_TOOLS_DIR)/go-server/src $(@D)/ + touch $@ + +#====================================================================== +# Generate swagger client in Python for yang generated OpenAPI specs +#====================================================================== +%.yangapi_client: $(YANGAPI_DIR)/$$(*F).yaml | $(CODEGEN_JAR) $$(@D)/. + @echo "+++++ Generating Python client for $(*F).yaml +++++" + java -jar $(CODEGEN_JAR) generate \ + -DpackageName=$(subst -,_,$(*F))_client \ + --lang python \ + --input-spec $(YANGAPI_DIR)/$(*F).yaml \ + --template-dir $(CODEGEN_TOOLS_DIR)/py-client/templates \ + --output $(@D) + touch $@ + +#====================================================================== +# Generate swagger client in Python for handcoded OpenAPI specs +#====================================================================== +%.openapi_client: $(OPENAPI_DIR)/$$(*F).yaml | $(CODEGEN_JAR) $$(@D)/. + @echo "+++++ Generating Python client for $(*F).yaml +++++" + java -jar $(CODEGEN_JAR) generate \ + -DpackageName=$(subst -,_,$(*F))_client \ + --lang python \ + --input-spec $(OPENAPI_DIR)/$(*F).yaml \ + --template-dir $(CODEGEN_TOOLS_DIR)/py-client/templates \ + --output $(@D) + touch $@ + +#====================================================================== +# Cleanups +#====================================================================== + +clean-server: + rm -rf $(SERVER_DIST_DIR) + rm -rf $(SERVER_CODEGEN_DIR) + +clean-client: + rm -rf $(PY_CLIENT_CODEGEN_DIR) + +clean: clean-server clean-client + make -C yang clean + +cleanall: clean + rm -f $(CODEGEN_JAR) + diff --git a/models/codegen.config b/models/codegen.config new file mode 100644 index 0000000000..fe64f2a6ce --- /dev/null +++ b/models/codegen.config @@ -0,0 +1,58 @@ +################################################################################ +# # +# 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. # +# # +################################################################################ + +# Build time configurations for swagger codegen +# Use makefile syntax + +## +# YANGAPI_EXCLUDES indicates the yang modules to be excluded from codegen. +# Server and client code will not be generated for these yangs modules. +# By default server code will be generated for all yangs under models/yang +# and models/yang/sonic directories. Note that each entry should be yang +# module name which is used for generated yaml file name. +YANGAPI_EXCLUDES += + +## +# PY_YANGAPI_CLIENTS indicates the yang modules for which python client +# sdk code should be generated. By default client sdk code will not be +# generated to save build time and space. YANGAPI_EXCLUDES has priority +# over this list. Note that the entry should be the yang module name +# which is used for generated yaml file name. +PY_YANGAPI_CLIENTS += openconfig-interfaces +PY_YANGAPI_CLIENTS += openconfig-lldp +PY_YANGAPI_CLIENTS += openconfig-platform +PY_YANGAPI_CLIENTS += openconfig-system +PY_YANGAPI_CLIENTS += openconfig-spanning-tree +PY_YANGAPI_CLIENTS += sonic-vxlan +PY_YANGAPI_CLIENTS += sonic-sflow + +## +# OPENAPI_EXCLUDES indicates the OpenAPI specs to be excluded from codegen. +# By default all yaml files under models/openapi directory are considered +# for codegen. Items should be the yaml file name without the .yaml extension. +# Eg: vlan.yaml should be specified as "OPENAPI_EXCLUDES += vlan" +OPENAPI_EXCLUDES += + +## +# PY_OPENAPI_CLIENTS indicates the OpenAPI specs for which python client +# sdk code should be generated. By default client sdk code is not generated. +# Items should be the yaml file name without the .yaml extension. Note +# that OPENAPI_EXCLUDES has priority over this list. +PY_OPENAPI_CLIENTS += + diff --git a/models/openapi/vlan.yaml.demo b/models/openapi/vlan.yaml.demo new file mode 100644 index 0000000000..8304bc3280 --- /dev/null +++ b/models/openapi/vlan.yaml.demo @@ -0,0 +1,186 @@ +swagger: "2.0" +info: + description: "Sample OpenAPI spec for SONiC VLAN table operations." + version: "1.0.0" + title: "SONiC Management Infra PoC - VLANs" + termsOfService: "" + contact: + email: "noone@broadcom.com" + license: + name: "Apache 2.0" + url: "http://www.apache.org/licenses/LICENSE-2.0.html" +basePath: "/nonyang" +tags: +- name: "vlan" + description: "Vlan configuration APIs" +schemes: +- "https" +- "http" +paths: + /vlan: + post: + tags: + - "vlan" + summary: "Create vlans" + description: "Create vlans by id" + operationId: "createVlans" + consumes: + - "application/json" + parameters: + - in: "body" + name: "body" + description: "Vlans to be configured" + required: true + schema: + type: "array" + items: + type: "integer" + responses: + 201: + description: "Vlans created" + 500: + description: "Vlan creation failed" + get: + tags: + - "vlan" + summary: "Get all vlan" + description: "Returns all vlans" + operationId: "getVlans" + produces: + - "application/json" + responses: + 200: + description: "successful operation" + schema: + type: "array" + items: + $ref: "#/definitions/VlanInfo" + 500: + description: "Internal error" + + /vlan/{id}: + get: + tags: + - "vlan" + summary: "Finds vlan by id" + description: "Returns vlan by id" + operationId: "getVlanById" + produces: + - "application/json" + parameters: + - name: "id" + in: "path" + description: "Vlan id" + required: true + type: "integer" + responses: + 200: + description: "successful operation" + schema: + $ref: "#/definitions/VlanInfo" + 404: + description: "Vlan not found" + delete: + tags: + - "vlan" + summary: "Delete vlan by id" + description: "Delete vlan by id" + operationId: "deleteVlanById" + parameters: + - name: "id" + in: "path" + description: "Vlan id" + required: true + type: "integer" + responses: + 204: + description: "Vlan deleted" + 404: + description: "Vlan not found" + + /vlan/{id}/member: + post: + tags: + - "vlan" + summary: "Add member interfaces" + description: "Add member interfaces" + operationId: "addVlanMembers" + consumes: + - "application/json" + parameters: + - name: "id" + in: "path" + description: "Vlan id" + required: true + type: "integer" + - name: "body" + in: "body" + description: "Member info" + required: true + schema: + type: "array" + items: + $ref: "#/definitions/VlanMember" + responses: + 201: + description: "Members added" + 404: + description: "Vlan not found" + + /vlan/{id}/member/{port}: + delete: + tags: + - "vlan" + summary: "Remove member interfaces" + description: "Remove member interfaces" + operationId: "removeVlanMembers" + parameters: + - name: "id" + in: "path" + description: "Vlan id" + required: true + type: "integer" + - name: "port" + in: "path" + description: "Member port name" + required: true + type: "string" + responses: + 204: + description: "Vlan member deleted" + 404: + description: "Vlan or vlan member config not found" + +definitions: + VlanInfo: + type: "object" + required: [id] + properties: + id: + description: "Vlan id" + type: "integer" + format: "int32" + name: + description: "Vlan name" + type: "string" + format: "string" + members: + description: "Vlan member port details" + type: "array" + items: + $ref: "#/definitions/VlanMember" + + VlanMember: + type: "object" + required: [port] + properties: + port: + description: "Member port name" + type: "string" + format: "string" + mode: + description: "Tagging mode" + type: "string" + enum: + - "tagged" + - "untagged" diff --git a/models/yang/.gitignore b/models/yang/.gitignore new file mode 100644 index 0000000000..d77ad18a85 --- /dev/null +++ b/models/yang/.gitignore @@ -0,0 +1,2 @@ +allyangs_tree.html +allyangs.tree diff --git a/models/yang/Makefile b/models/yang/Makefile new file mode 100644 index 0000000000..60555c873c --- /dev/null +++ b/models/yang/Makefile @@ -0,0 +1,84 @@ +################################################################################ +# # +# 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 + +YANGAPI_DIR := $(TOPDIR)/build/yaml +YANGDIR := $(TOPDIR)/models/yang +YANGDIR_COMMON := $(TOPDIR)/models/yang/common +YANGDIR_OPENAPI := $(TOPDIR)/models/openapi +YANGDIR_ANNOTATIONS := $(TOPDIR)/models/yang/annotations +YANG_MOD_FILES := $(shell find $(YANGDIR) -maxdepth 1 -name '*.yang' | sort) +YANG_ANNOTATIONS_FILES := $(shell find $(YANGDIR_ANNOTATIONS) -name '*.yang' | sort) +YANG_COMMON_FILES := $(shell find $(YANGDIR_COMMON) -name '*.yang' | sort) + +TOOLS_DIR := $(TOPDIR)/tools +PYANG_DIR := $(TOOLS_DIR)/pyang +PYANG_PLUGIN_DIR := $(PYANG_DIR)/pyang_plugins +PYANG_BIN := pyang + +.PHONY: all yamlGen annot + +all: yamlGen annot allyangs.tree allyangs_tree.html + +yamlGen: $(YANGAPI_DIR)/.done + +annot: + $(PYANG_BIN) --strict \ + -p $(YANGDIR_COMMON):$(YANGDIR) $(YANG_ANNOTATIONS_FILES) + +allyangs.tree: $(YANG_MOD_FILES) $(YANG_COMMON_FILES) + $(PYANG_BIN) \ + -f tree \ + -o $(YANGDIR)/$@ \ + -p $(YANGDIR_COMMON):$(YANGDIR) \ + $(YANG_MOD_FILES) + @echo "+++++ Generation of YANG tree for Yang modules completed +++++" + +allyangs_tree.html: $(YANG_MOD_FILES) $(YANG_COMMON_FILES) + $(PYANG_BIN) \ + -f jstree \ + -o $(YANGDIR)/$@ \ + -p $(YANGDIR_COMMON):$(YANGDIR) \ + $(YANG_MOD_FILES) + @echo "+++++ Generation of HTML tree for Yang modules completed +++++" + +#====================================================================== +# Generate YAML files for Yang modules +#====================================================================== +$(YANGAPI_DIR)/.done: $(YANG_MOD_FILES) $(YANG_COMMON_FILES) + @echo "+++++ Generating YAML files for Yang modules +++++" + mkdir -p $(YANGAPI_DIR) + $(PYANG_BIN) \ + -f swaggerapi \ + --outdir $(YANGAPI_DIR) \ + --plugindir $(PYANG_PLUGIN_DIR) \ + -p $(YANGDIR_COMMON):$(YANGDIR) \ + $(YANG_MOD_FILES) + @echo "+++++ Generation of YAML files for Yang modules completed +++++" + touch $@ + +#====================================================================== +# Cleanups +#====================================================================== + +clean: + rm -rf $(YANGAPI_DIR) + rm -rf allyangs.tree allyangs_tree.html diff --git a/models/yang/annotations/ietf-ptp-annot.yang b/models/yang/annotations/ietf-ptp-annot.yang new file mode 100644 index 0000000000..21d5048ade --- /dev/null +++ b/models/yang/annotations/ietf-ptp-annot.yang @@ -0,0 +1,191 @@ +module ietf-ptp-annot { + + yang-version "1.1"; + + namespace "http://openconfig.net/yang/annotation/ptp-annot"; + prefix "ptp-annot"; + + import sonic-extensions { prefix sonic-ext; } + import ietf-interfaces { prefix if; } + import ietf-ptp { prefix ptp; } + + deviation /ptp:ptp { + deviate add { + } + } + + deviation /ptp:ptp/ptp:instance-list { + deviate add { + sonic-ext:table-name "PTP_INSTANCE"; + sonic-ext:key-transformer "ptp_global_key_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:current-ds { + deviate add { + sonic-ext:table-name "PTP_CURRENTDS"; + sonic-ext:key-transformer "ptp_global_key_xfmr"; + sonic-ext:db-name "STATE_DB"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:parent-ds { + deviate add { + sonic-ext:table-name "PTP_PARENTDS"; + sonic-ext:key-transformer "ptp_global_key_xfmr"; + sonic-ext:db-name "STATE_DB"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:parent-ds/ptp:grandmaster-identity { + deviate add { + sonic-ext:field-transformer "ptp_clock_identity_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:parent-ds/ptp:parent-port-identity/ptp:clock-identity { + deviate add { + sonic-ext:field-transformer "ptp_clock_identity_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:parent-ds/ptp:grandmaster-clock-quality/ptp:offset-scaled-log-variance { + deviate add { + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:parent-ds/ptp:parent-stats { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds { + deviate add { + sonic-ext:table-name "PTP_TIMEPROPDS"; + sonic-ext:key-transformer "ptp_global_key_xfmr"; + sonic-ext:db-name "STATE_DB"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds/ptp:time-traceable { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds/ptp:frequency-traceable { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds/ptp:ptp-timescale { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds/ptp:current-utc-offset-valid { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds/ptp:leap59 { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:time-properties-ds/ptp:leap61 { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:port-ds-list { + deviate add { + sonic-ext:table-name "PTP_PORT"; + sonic-ext:key-transformer "ptp_port_entry_key_xfmr"; + sonic-ext:db-name "STATE_DB"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:port-ds-list/ptp:port-state { + deviate add { + sonic-ext:field-transformer "ptp_port_state_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:port-ds-list/ptp:delay-mechanism { + deviate add { + sonic-ext:field-transformer "ptp_delay_mech_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:instance-number { + deviate add { + sonic-ext:field-transformer "ptp_inst_number_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:default-ds { + deviate add { + sonic-ext:table-name "PTP_CLOCK"; + sonic-ext:key-transformer "ptp_entry_key_xfmr"; + sonic-ext:db-name "STATE_DB"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:default-ds/ptp:slave-only { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:default-ds/ptp:two-step-flag { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:instance-list/ptp:default-ds/ptp:clock-identity { + deviate add { + sonic-ext:field-transformer "ptp_clock_identity_xfmr"; + } + } + + deviation /ptp:ptp/ptp:transparent-clock-port-ds-list { + deviate add { + sonic-ext:table-name "PTP_TC_PORT"; + sonic-ext:key-transformer "ptp_tcport_entry_key_xfmr"; + } + } + + deviation /ptp:ptp/ptp:transparent-clock-port-ds-list/ptp:faulty-flag { + deviate add { + sonic-ext:field-transformer "ptp_boolean_xfmr"; + } + } + + deviation /ptp:ptp/ptp:transparent-clock-default-ds { + deviate add { + sonic-ext:table-name "PTP_TC_CLOCK"; + sonic-ext:key-transformer "ptp_global_key_xfmr"; + } + } + + deviation /ptp:ptp/ptp:transparent-clock-default-ds/ptp:clock-identity { + deviate add { + sonic-ext:field-transformer "ptp_clock_identity_xfmr"; + } + } + + deviation /ptp:ptp/ptp:transparent-clock-default-ds/ptp:delay-mechanism { + deviate add { + sonic-ext:field-transformer "ptp_delay_mech_xfmr"; + } + } + +} diff --git a/models/yang/annotations/openconfig-acl-annot.yang b/models/yang/annotations/openconfig-acl-annot.yang new file mode 100644 index 0000000000..e7e77ab7e8 --- /dev/null +++ b/models/yang/annotations/openconfig-acl-annot.yang @@ -0,0 +1,211 @@ +module openconfig-acl-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/acl-annot"; + prefix "oc-acl-annot"; + + import sonic-extensions { prefix sonic-ext; } + import openconfig-acl { prefix oc-acl; } + + // meta + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN"; + + revision 2019-10-31 { + description + "Initial revision."; + } + + deviation /oc-acl:acl { + deviate add { + sonic-ext:post-transformer "acl_post_xfmr"; + } + } + + deviation /oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set { + deviate add { + sonic-ext:table-name "ACL_TABLE"; + sonic-ext:key-transformer "acl_set_key_xfmr"; + } + } + + 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-acl:ipv4/oc-acl:config/oc-acl: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-acl:ipv4/oc-acl:config/oc-acl: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-acl:ipv4/oc-acl:config/oc-acl: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-acl:ipv4/oc-acl:state/oc-acl: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-acl:ipv4/oc-acl:state/oc-acl: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-acl:ipv4/oc-acl:state/oc-acl: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-acl:ipv6/oc-acl:config/oc-acl: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-acl:ipv6/oc-acl:config/oc-acl: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-acl:ipv6/oc-acl:config/oc-acl: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-acl:transport/oc-acl:config/oc-acl: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-acl:transport/oc-acl:config/oc-acl: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-acl:transport/oc-acl:state/oc-acl: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-acl:transport/oc-acl:state/oc-acl: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-acl:transport/oc-acl:config/oc-acl: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-acl:l2/oc-acl:config/oc-acl: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/annotations/openconfig-network-instance-annot.yang b/models/yang/annotations/openconfig-network-instance-annot.yang new file mode 100644 index 0000000000..09689d4385 --- /dev/null +++ b/models/yang/annotations/openconfig-network-instance-annot.yang @@ -0,0 +1,56 @@ +module openconfig-network-instance-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/annotation"; + prefix "oc-netinst-annot"; + + import openconfig-network-instance { prefix oc-netinst; } + import openconfig-network-instance-l3 { prefix oc-ni-l3; } + import openconfig-types { prefix oc-types; } + import openconfig-mpls { prefix oc-mpls; } + import openconfig-vlan { prefix oc-vlan; } + import openconfig-network-instance-types { prefix oc-ni-types; } + import openconfig-policy-types { prefix oc-pol-types; } + import openconfig-local-routing { prefix oc-loc-rt; } + import openconfig-interfaces { prefix oc-if; } + import ietf-inet-types { prefix inet; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-pim { prefix oc-pim; } + import openconfig-routing-policy { prefix oc-rpol; } + import openconfig-bgp { prefix oc-bgp; } + import openconfig-policy-forwarding { prefix oc-pf; } + import openconfig-isis { prefix oc-isis; } + import openconfig-igmp { prefix oc-igmp; } + import ietf-yang-types { prefix yang; } + import openconfig-ospfv2 { prefix oc-ospfv2; } + import openconfig-segment-routing { prefix oc-sr; } + import openconfig-aft { prefix oc-aft; } + import sonic-extensions { prefix sonic-ext; } + + deviation /oc-netinst:network-instances/oc-netinst:network-instance/oc-netinst:fdb/oc-netinst:mac-table/oc-netinst:entries { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + + deviation /oc-netinst:network-instances/oc-netinst:network-instance/oc-netinst:fdb/oc-netinst:mac-table/oc-netinst:entries/oc-netinst:entry { + deviate add { + sonic-ext:table-name "FDB_TABLE"; + sonic-ext:key-transformer "fdb_tbl_key_xfmr"; + } + } + + deviation /oc-netinst:network-instances/oc-netinst:network-instance/oc-netinst:fdb/oc-netinst:mac-table/oc-netinst:entries/oc-netinst:entry/oc-netinst:state/oc-netinst:entry-type { + deviate add { + sonic-ext:field-transformer "entry_type_field_xfmr"; + } + } + + deviation /oc-netinst:network-instances/oc-netinst:network-instance/oc-netinst:fdb/oc-netinst:mac-table/oc-netinst:entries/oc-netinst:entry/oc-netinst:interface/oc-netinst:interface-ref/oc-netinst:state/oc-netinst:interface { + deviate add { + sonic-ext:field-name "port"; + } + } +} + diff --git a/models/yang/annotations/openconfig-system-annot.yang b/models/yang/annotations/openconfig-system-annot.yang new file mode 100644 index 0000000000..b97d645128 --- /dev/null +++ b/models/yang/annotations/openconfig-system-annot.yang @@ -0,0 +1,31 @@ +module openconfig-system-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/openconfig-system-annot"; + prefix "oc-sys-annot"; + + import openconfig-system { prefix oc-sys; } + import sonic-extensions {prefix sonic-ext; } + + deviation /oc-sys:system/oc-sys:state { + deviate add { + sonic-ext:subtree-transformer "sys_state_xfmr"; + } + } + deviation /oc-sys:system/oc-sys:memory { + deviate add { + sonic-ext:subtree-transformer "sys_memory_xfmr"; + } + } + deviation /oc-sys:system/oc-sys:cpus { + deviate add { + sonic-ext:subtree-transformer "sys_cpus_xfmr"; + } + } + deviation /oc-sys:system/oc-sys:processes { + deviate add { + sonic-ext:subtree-transformer "sys_procs_xfmr"; + } + } +} diff --git a/models/yang/annotations/sonic-extensions.yang b/models/yang/annotations/sonic-extensions.yang new file mode 100644 index 0000000000..e8c7e450c6 --- /dev/null +++ b/models/yang/annotations/sonic-extensions.yang @@ -0,0 +1,95 @@ +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 openapi-opid { + argument "openapi-opid"; + description "Custom Operation ID for OpenAPI"; + } + + 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."; + } + + extension rpc-callback { + argument "callback"; + description "RPC callback to be invoked for action"; + } + +} diff --git a/models/yang/annotations/sonic-port-annot.yang b/models/yang/annotations/sonic-port-annot.yang new file mode 100644 index 0000000000..bc7b05188b --- /dev/null +++ b/models/yang/annotations/sonic-port-annot.yang @@ -0,0 +1,18 @@ +module sonic-port-annot { + + yang-version "1.1"; + + namespace "http://openconfig.net/yang/annotation/prt-annot"; + prefix "prt-annot"; + + import sonic-extensions { prefix sonic-ext; } + import sonic-port { prefix prt; } + + deviation /prt:sonic-port/prt:PORT_TABLE { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + +} + diff --git a/models/yang/annotations/sonic-portchannel-annot.yang b/models/yang/annotations/sonic-portchannel-annot.yang new file mode 100644 index 0000000000..ce3712556f --- /dev/null +++ b/models/yang/annotations/sonic-portchannel-annot.yang @@ -0,0 +1,24 @@ +module sonic-portchannel-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/annotation/spc-annot"; + prefix "spc-annot"; + + import sonic-portchannel { prefix spc; } + import sonic-extensions { prefix sonic-ext; } + + + deviation /spc:sonic-portchannel/spc:LAG_TABLE { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + + deviation /spc:sonic-portchannel/spc:LAG_MEMBER_TABLE { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } +} + diff --git a/models/yang/annotations/sonic-sflow-annot.yang b/models/yang/annotations/sonic-sflow-annot.yang new file mode 100644 index 0000000000..8f6f799fe2 --- /dev/null +++ b/models/yang/annotations/sonic-sflow-annot.yang @@ -0,0 +1,23 @@ +module sonic-sflow-annot { + + yang-version "1.1"; + + namespace "http://openconfig.net/yang/annotation/sflow-annot"; + prefix "sflow-annot"; + + import ietf-yang-types { prefix yang; } + import ietf-inet-types { prefix inet; } + import sonic-common { prefix scommon; } + import sonic-sflow { prefix sflow; } + import sonic-extensions { prefix sonic-ext; } + + + deviation /sflow:sonic-sflow/sflow:SFLOW_SESSION_TABLE/sflow:SFLOW_SESSION_LIST { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + + +} + diff --git a/models/yang/annotations/sonic-udld-annot.yang b/models/yang/annotations/sonic-udld-annot.yang new file mode 100644 index 0000000000..2c2cf73748 --- /dev/null +++ b/models/yang/annotations/sonic-udld-annot.yang @@ -0,0 +1,36 @@ +module sonic-udld-annot { + + yang-version "1.1"; + + namespace "http://openconfig.net/yang/annotation/udld-annot"; + prefix "udld-annot"; + + import sonic-extensions { prefix sonic-ext; } + import sonic-udld { prefix udld; } + + + deviation /udld:sonic-udld/udld:UDLD/udld:UDLD_LIST { + deviate add { + sonic-ext:table-name "UDLD"; + sonic-ext:key-transformer "udld_global_key_xfmr"; + } + } + + deviation /udld:sonic-udld/udld:UDLD_GLOBAL_TABLE/udld:UDLD_GLOBAL_TABLE_LIST { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + + deviation /udld:sonic-udld/udld:UDLD_PORT_TABLE/udld:UDLD_PORT_TABLE_LIST { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + + deviation /udld:sonic-udld/udld:UDLD_PORT_NEIGH_TABLE/udld:UDLD_PORT_NEIGH_TABLE_LIST { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } +} diff --git a/models/yang/annotations/sonic-vlan-annot.yang b/models/yang/annotations/sonic-vlan-annot.yang new file mode 100644 index 0000000000..6f86db1dd0 --- /dev/null +++ b/models/yang/annotations/sonic-vlan-annot.yang @@ -0,0 +1,38 @@ +module sonic-vlan-annot { + + yang-version "1.1"; + + namespace "http://openconfig.net/yang/vlan-annot"; + prefix "svlan-annot"; + + import sonic-extensions { prefix sonic-ext; } + import sonic-vlan { prefix svlan; } + + // meta + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN"; + + revision 2019-10-31 { + description + "Initial revision."; + } + + deviation /svlan:sonic-vlan/svlan:VLAN_TABLE/svlan:VLAN_TABLE_LIST { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } + + deviation /svlan:sonic-vlan/svlan:VLAN_MEMBER_TABLE/svlan:VLAN_MEMBER_TABLE_LIST { + deviate add { + sonic-ext:db-name "APPL_DB"; + } + } +} + diff --git a/models/yang/common/iana-if-type.yang b/models/yang/common/iana-if-type.yang new file mode 100644 index 0000000000..74e46b4b2f --- /dev/null +++ b/models/yang/common/iana-if-type.yang @@ -0,0 +1,1554 @@ +module iana-if-type { + namespace "urn:ietf:params:xml:ns:yang:iana-if-type"; + prefix ianaift; + + import ietf-interfaces { + prefix if; + } + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States + + Tel: +1 310 301 5800 + "; + description + "This YANG module defines YANG identities for IANA-registered + interface types. + + This YANG module is maintained by IANA and reflects the + 'ifType definitions' registry. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Requests for new values should be made to IANA via + email (iana&iana.org). + + Copyright (c) 2014 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). + + The initial version of this YANG module is part of RFC 7224; + see the RFC itself for full legal notices."; + reference + "IANA 'ifType definitions' registry. + "; + + revision 2015-06-12 { + description + "Corrected formatting issue."; + } + revision 2014-09-24 { + description + "Registered ifType 280."; + } + revision 2014-09-19 { + description + "Registered ifType 279."; + } + revision 2014-07-03 { + description + "Registered ifTypes 277-278."; + } + revision 2014-05-19 { + description + "Updated the contact address."; + } + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7224: IANA Interface Type YANG Module"; + } + + identity iana-interface-type { + base if:interface-type; + description + "This identity is used as a base for all interface types + defined in the 'ifType definitions' registry."; + } + + identity other { + base iana-interface-type; + } + identity regular1822 { + base iana-interface-type; + } + identity hdh1822 { + base iana-interface-type; + } + identity ddnX25 { + base iana-interface-type; + } + identity rfc877x25 { + base iana-interface-type; + reference + "RFC 1382 - SNMP MIB Extension for the X.25 Packet Layer"; + } + identity ethernetCsmacd { + base iana-interface-type; + description + "For all Ethernet-like interfaces, regardless of speed, + as per RFC 3635."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity iso88023Csmacd { + base iana-interface-type; + status deprecated; + description + "Deprecated via RFC 3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity iso88024TokenBus { + base iana-interface-type; + } + identity iso88025TokenRing { + base iana-interface-type; + } + identity iso88026Man { + base iana-interface-type; + } + identity starLan { + base iana-interface-type; + status deprecated; + description + "Deprecated via RFC 3635. + Use ethernetCsmacd(6) instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity proteon10Mbit { + base iana-interface-type; + } + identity proteon80Mbit { + base iana-interface-type; + } + identity hyperchannel { + base iana-interface-type; + } + identity fddi { + base iana-interface-type; + reference + "RFC 1512 - FDDI Management Information Base"; + } + identity lapb { + base iana-interface-type; + reference + "RFC 1381 - SNMP MIB Extension for X.25 LAPB"; + } + identity sdlc { + base iana-interface-type; + } + identity ds1 { + base iana-interface-type; + description + "DS1-MIB."; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + identity e1 { + base iana-interface-type; + status obsolete; + description + "Obsolete; see DS1-MIB."; + reference + "RFC 4805 - Definitions of Managed Objects for the + DS1, J1, E1, DS2, and E2 Interface Types"; + } + identity basicISDN { + base iana-interface-type; + description + "No longer used. See also RFC 2127."; + } + identity primaryISDN { + base iana-interface-type; + description + "No longer used. See also RFC 2127."; + } + identity propPointToPointSerial { + base iana-interface-type; + description + "Proprietary serial."; + } + identity ppp { + base iana-interface-type; + } + identity softwareLoopback { + base iana-interface-type; + } + identity eon { + base iana-interface-type; + description + "CLNP over IP."; + } + identity ethernet3Mbit { + base iana-interface-type; + } + identity nsip { + base iana-interface-type; + description + "XNS over IP."; + } + identity slip { + base iana-interface-type; + description + "Generic SLIP."; + } + identity ultra { + base iana-interface-type; + description + "Ultra Technologies."; + } + identity ds3 { + base iana-interface-type; + description + "DS3-MIB."; + reference + "RFC 3896 - Definitions of Managed Objects for the + DS3/E3 Interface Type"; + } + identity sip { + base iana-interface-type; + description + "SMDS, coffee."; + reference + "RFC 1694 - Definitions of Managed Objects for SMDS + Interfaces using SMIv2"; + } + identity frameRelay { + base iana-interface-type; + description + "DTE only."; + reference + "RFC 2115 - Management Information Base for Frame Relay + DTEs Using SMIv2"; + } + identity rs232 { + base iana-interface-type; + reference + "RFC 1659 - Definitions of Managed Objects for RS-232-like + Hardware Devices using SMIv2"; + } + identity para { + base iana-interface-type; + description + "Parallel-port."; + reference + "RFC 1660 - Definitions of Managed Objects for + Parallel-printer-like Hardware Devices using + SMIv2"; + } + identity arcnet { + base iana-interface-type; + description + "ARCnet."; + } + identity arcnetPlus { + base iana-interface-type; + description + "ARCnet Plus."; + } + identity atm { + base iana-interface-type; + description + "ATM cells."; + } + identity miox25 { + base iana-interface-type; + reference + "RFC 1461 - SNMP MIB extension for Multiprotocol + Interconnect over X.25"; + } + identity sonet { + base iana-interface-type; + description + "SONET or SDH."; + } + identity x25ple { + base iana-interface-type; + reference + "RFC 2127 - ISDN Management Information Base using SMIv2"; + } + identity iso88022llc { + base iana-interface-type; + } + identity localTalk { + base iana-interface-type; + } + identity smdsDxi { + base iana-interface-type; + } + identity frameRelayService { + base iana-interface-type; + description + "FRNETSERV-MIB."; + reference + "RFC 2954 - Definitions of Managed Objects for Frame + Relay Service"; + } + identity v35 { + base iana-interface-type; + } + identity hssi { + base iana-interface-type; + } + identity hippi { + base iana-interface-type; + } + identity modem { + base iana-interface-type; + description + "Generic modem."; + } + identity aal5 { + base iana-interface-type; + description + "AAL5 over ATM."; + } + identity sonetPath { + base iana-interface-type; + } + identity sonetVT { + base iana-interface-type; + } + identity smdsIcip { + base iana-interface-type; + description + "SMDS InterCarrier Interface."; + } + identity propVirtual { + base iana-interface-type; + description + "Proprietary virtual/internal."; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + identity propMultiplexor { + base iana-interface-type; + description + "Proprietary multiplexing."; + reference + "RFC 2863 - The Interfaces Group MIB"; + } + identity ieee80212 { + base iana-interface-type; + description + "100BaseVG."; + } + identity fibreChannel { + base iana-interface-type; + description + "Fibre Channel."; + } + identity hippiInterface { + base iana-interface-type; + description + "HIPPI interfaces."; + } + identity frameRelayInterconnect { + base iana-interface-type; + status obsolete; + description + "Obsolete; use either + frameRelay(32) or frameRelayService(44)."; + } + identity aflane8023 { + base iana-interface-type; + description + "ATM Emulated LAN for 802.3."; + } + identity aflane8025 { + base iana-interface-type; + description + "ATM Emulated LAN for 802.5."; + } + identity cctEmul { + base iana-interface-type; + description + "ATM Emulated circuit."; + } + identity fastEther { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity isdn { + base iana-interface-type; + description + "ISDN and X.25."; + reference + "RFC 1356 - Multiprotocol Interconnect on X.25 and ISDN + in the Packet Mode"; + } + identity v11 { + base iana-interface-type; + description + "CCITT V.11/X.21."; + } + identity v36 { + base iana-interface-type; + description + "CCITT V.36."; + } + identity g703at64k { + base iana-interface-type; + description + "CCITT G703 at 64Kbps."; + } + identity g703at2mb { + base iana-interface-type; + status obsolete; + description + "Obsolete; see DS1-MIB."; + } + identity qllc { + base iana-interface-type; + description + "SNA QLLC."; + } + identity fastEtherFX { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity channel { + base iana-interface-type; + description + "Channel."; + } + identity ieee80211 { + base iana-interface-type; + description + "Radio spread spectrum."; + } + identity ibm370parChan { + base iana-interface-type; + description + "IBM System 360/370 OEMI Channel."; + } + identity escon { + base iana-interface-type; + description + "IBM Enterprise Systems Connection."; + } + identity dlsw { + base iana-interface-type; + description + "Data Link Switching."; + } + identity isdns { + base iana-interface-type; + description + "ISDN S/T interface."; + } + identity isdnu { + base iana-interface-type; + description + "ISDN U interface."; + } + identity lapd { + base iana-interface-type; + description + "Link Access Protocol D."; + } + identity ipSwitch { + base iana-interface-type; + description + "IP Switching Objects."; + } + identity rsrb { + base iana-interface-type; + description + "Remote Source Route Bridging."; + } + identity atmLogical { + base iana-interface-type; + description + "ATM Logical Port."; + reference + "RFC 3606 - Definitions of Supplemental Managed Objects + for ATM Interface"; + } + identity ds0 { + base iana-interface-type; + description + "Digital Signal Level 0."; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + identity ds0Bundle { + base iana-interface-type; + description + "Group of ds0s on the same ds1."; + reference + "RFC 2494 - Definitions of Managed Objects for the DS0 + and DS0 Bundle Interface Type"; + } + identity bsc { + base iana-interface-type; + description + "Bisynchronous Protocol."; + } + identity async { + base iana-interface-type; + description + "Asynchronous Protocol."; + } + identity cnr { + base iana-interface-type; + description + "Combat Net Radio."; + } + identity iso88025Dtr { + base iana-interface-type; + description + "ISO 802.5r DTR."; + } + identity eplrs { + base iana-interface-type; + description + "Ext Pos Loc Report Sys."; + } + identity arap { + base iana-interface-type; + description + "Appletalk Remote Access Protocol."; + } + identity propCnls { + base iana-interface-type; + description + "Proprietary Connectionless Protocol."; + } + identity hostPad { + base iana-interface-type; + description + "CCITT-ITU X.29 PAD Protocol."; + } + identity termPad { + base iana-interface-type; + description + "CCITT-ITU X.3 PAD Facility."; + } + identity frameRelayMPI { + base iana-interface-type; + description + "Multiproto Interconnect over FR."; + } + identity x213 { + base iana-interface-type; + description + "CCITT-ITU X213."; + } + identity adsl { + base iana-interface-type; + description + "Asymmetric Digital Subscriber Loop."; + } + identity radsl { + base iana-interface-type; + description + "Rate-Adapt. Digital Subscriber Loop."; + } + identity sdsl { + base iana-interface-type; + description + "Symmetric Digital Subscriber Loop."; + } + identity vdsl { + base iana-interface-type; + description + "Very H-Speed Digital Subscrib. Loop."; + } + identity iso88025CRFPInt { + base iana-interface-type; + description + "ISO 802.5 CRFP."; + } + identity myrinet { + base iana-interface-type; + description + "Myricom Myrinet."; + } + identity voiceEM { + base iana-interface-type; + description + "Voice recEive and transMit."; + } + identity voiceFXO { + base iana-interface-type; + description + "Voice Foreign Exchange Office."; + } + identity voiceFXS { + base iana-interface-type; + description + "Voice Foreign Exchange Station."; + } + identity voiceEncap { + base iana-interface-type; + description + "Voice encapsulation."; + } + identity voiceOverIp { + base iana-interface-type; + description + "Voice over IP encapsulation."; + } + identity atmDxi { + base iana-interface-type; + description + "ATM DXI."; + } + identity atmFuni { + base iana-interface-type; + description + "ATM FUNI."; + } + identity atmIma { + base iana-interface-type; + description + "ATM IMA."; + } + identity pppMultilinkBundle { + base iana-interface-type; + description + "PPP Multilink Bundle."; + } + identity ipOverCdlc { + base iana-interface-type; + description + "IBM ipOverCdlc."; + } + identity ipOverClaw { + base iana-interface-type; + description + "IBM Common Link Access to Workstn."; + } + identity stackToStack { + base iana-interface-type; + description + "IBM stackToStack."; + } + identity virtualIpAddress { + base iana-interface-type; + description + "IBM VIPA."; + } + identity mpc { + base iana-interface-type; + description + "IBM multi-protocol channel support."; + } + identity ipOverAtm { + base iana-interface-type; + description + "IBM ipOverAtm."; + reference + "RFC 2320 - Definitions of Managed Objects for Classical IP + and ARP Over ATM Using SMIv2 (IPOA-MIB)"; + } + identity iso88025Fiber { + base iana-interface-type; + description + "ISO 802.5j Fiber Token Ring."; + } + identity tdlc { + base iana-interface-type; + description + "IBM twinaxial data link control."; + } + identity gigabitEthernet { + base iana-interface-type; + status deprecated; + description + "Obsoleted via RFC 3635. + ethernetCsmacd(6) should be used instead."; + reference + "RFC 3635 - Definitions of Managed Objects for the + Ethernet-like Interface Types"; + } + identity hdlc { + base iana-interface-type; + description + "HDLC."; + } + identity lapf { + base iana-interface-type; + description + "LAP F."; + } + identity v37 { + base iana-interface-type; + description + "V.37."; + } + identity x25mlp { + base iana-interface-type; + description + "Multi-Link Protocol."; + } + identity x25huntGroup { + base iana-interface-type; + description + "X25 Hunt Group."; + } + identity transpHdlc { + base iana-interface-type; + description + "Transp HDLC."; + } + identity interleave { + base iana-interface-type; + description + "Interleave channel."; + } + identity fast { + base iana-interface-type; + description + "Fast channel."; + } + identity ip { + base iana-interface-type; + description + "IP (for APPN HPR in IP networks)."; + } + identity docsCableMaclayer { + base iana-interface-type; + description + "CATV Mac Layer."; + } + identity docsCableDownstream { + base iana-interface-type; + description + "CATV Downstream interface."; + } + identity docsCableUpstream { + base iana-interface-type; + description + "CATV Upstream interface."; + } + identity a12MppSwitch { + base iana-interface-type; + description + "Avalon Parallel Processor."; + } + identity tunnel { + base iana-interface-type; + description + "Encapsulation interface."; + } + identity coffee { + base iana-interface-type; + description + "Coffee pot."; + reference + "RFC 2325 - Coffee MIB"; + } + identity ces { + base iana-interface-type; + description + "Circuit Emulation Service."; + } + identity atmSubInterface { + base iana-interface-type; + description + "ATM Sub Interface."; + } + identity l2vlan { + base iana-interface-type; + description + "Layer 2 Virtual LAN using 802.1Q."; + } + identity l3ipvlan { + base iana-interface-type; + description + "Layer 3 Virtual LAN using IP."; + } + identity l3ipxvlan { + base iana-interface-type; + description + "Layer 3 Virtual LAN using IPX."; + } + identity digitalPowerline { + base iana-interface-type; + description + "IP over Power Lines."; + } + identity mediaMailOverIp { + base iana-interface-type; + description + "Multimedia Mail over IP."; + } + identity dtm { + base iana-interface-type; + description + "Dynamic synchronous Transfer Mode."; + } + identity dcn { + base iana-interface-type; + description + "Data Communications Network."; + } + identity ipForward { + base iana-interface-type; + description + "IP Forwarding Interface."; + } + identity msdsl { + base iana-interface-type; + description + "Multi-rate Symmetric DSL."; + } + identity ieee1394 { + base iana-interface-type; + + description + "IEEE1394 High Performance Serial Bus."; + } + identity if-gsn { + base iana-interface-type; + description + "HIPPI-6400."; + } + identity dvbRccMacLayer { + base iana-interface-type; + description + "DVB-RCC MAC Layer."; + } + identity dvbRccDownstream { + base iana-interface-type; + description + "DVB-RCC Downstream Channel."; + } + identity dvbRccUpstream { + base iana-interface-type; + description + "DVB-RCC Upstream Channel."; + } + identity atmVirtual { + base iana-interface-type; + description + "ATM Virtual Interface."; + } + identity mplsTunnel { + base iana-interface-type; + description + "MPLS Tunnel Virtual Interface."; + } + identity srp { + base iana-interface-type; + description + "Spatial Reuse Protocol."; + } + identity voiceOverAtm { + base iana-interface-type; + description + "Voice over ATM."; + } + identity voiceOverFrameRelay { + base iana-interface-type; + description + "Voice Over Frame Relay."; + } + identity idsl { + base iana-interface-type; + description + "Digital Subscriber Loop over ISDN."; + } + identity compositeLink { + base iana-interface-type; + description + "Avici Composite Link Interface."; + } + identity ss7SigLink { + base iana-interface-type; + description + "SS7 Signaling Link."; + } + identity propWirelessP2P { + base iana-interface-type; + description + "Prop. P2P wireless interface."; + } + identity frForward { + base iana-interface-type; + description + "Frame Forward Interface."; + } + identity rfc1483 { + base iana-interface-type; + description + "Multiprotocol over ATM AAL5."; + reference + "RFC 1483 - Multiprotocol Encapsulation over ATM + Adaptation Layer 5"; + } + identity usb { + base iana-interface-type; + description + "USB Interface."; + } + identity ieee8023adLag { + base iana-interface-type; + description + "IEEE 802.3ad Link Aggregate."; + } + identity bgppolicyaccounting { + base iana-interface-type; + description + "BGP Policy Accounting."; + } + identity frf16MfrBundle { + base iana-interface-type; + description + "FRF.16 Multilink Frame Relay."; + } + identity h323Gatekeeper { + base iana-interface-type; + description + "H323 Gatekeeper."; + } + identity h323Proxy { + base iana-interface-type; + description + "H323 Voice and Video Proxy."; + } + identity mpls { + base iana-interface-type; + description + "MPLS."; + } + identity mfSigLink { + base iana-interface-type; + description + "Multi-frequency signaling link."; + } + identity hdsl2 { + base iana-interface-type; + description + "High Bit-Rate DSL - 2nd generation."; + } + identity shdsl { + base iana-interface-type; + description + "Multirate HDSL2."; + } + identity ds1FDL { + base iana-interface-type; + description + "Facility Data Link (4Kbps) on a DS1."; + } + identity pos { + base iana-interface-type; + description + "Packet over SONET/SDH Interface."; + } + identity dvbAsiIn { + base iana-interface-type; + description + "DVB-ASI Input."; + } + identity dvbAsiOut { + base iana-interface-type; + description + "DVB-ASI Output."; + } + identity plc { + base iana-interface-type; + description + "Power Line Communications."; + } + identity nfas { + base iana-interface-type; + description + "Non-Facility Associated Signaling."; + } + identity tr008 { + base iana-interface-type; + description + "TR008."; + } + identity gr303RDT { + base iana-interface-type; + description + "Remote Digital Terminal."; + } + identity gr303IDT { + base iana-interface-type; + description + "Integrated Digital Terminal."; + } + identity isup { + base iana-interface-type; + description + "ISUP."; + } + identity propDocsWirelessMaclayer { + base iana-interface-type; + description + "Cisco proprietary Maclayer."; + } + identity propDocsWirelessDownstream { + base iana-interface-type; + description + "Cisco proprietary Downstream."; + } + identity propDocsWirelessUpstream { + base iana-interface-type; + description + "Cisco proprietary Upstream."; + } + identity hiperlan2 { + base iana-interface-type; + description + "HIPERLAN Type 2 Radio Interface."; + } + identity propBWAp2Mp { + base iana-interface-type; + description + "PropBroadbandWirelessAccesspt2Multipt (use of this value + for IEEE 802.16 WMAN interfaces as per IEEE Std 802.16f + is deprecated, and ieee80216WMAN(237) should be used + instead)."; + } + identity sonetOverheadChannel { + base iana-interface-type; + description + "SONET Overhead Channel."; + } + identity digitalWrapperOverheadChannel { + base iana-interface-type; + description + "Digital Wrapper."; + } + identity aal2 { + base iana-interface-type; + description + "ATM adaptation layer 2."; + } + identity radioMAC { + base iana-interface-type; + description + "MAC layer over radio links."; + } + identity atmRadio { + base iana-interface-type; + description + "ATM over radio links."; + } + identity imt { + base iana-interface-type; + description + "Inter-Machine Trunks."; + } + identity mvl { + base iana-interface-type; + description + "Multiple Virtual Lines DSL."; + } + identity reachDSL { + base iana-interface-type; + description + "Long Reach DSL."; + } + identity frDlciEndPt { + base iana-interface-type; + description + "Frame Relay DLCI End Point."; + } + identity atmVciEndPt { + base iana-interface-type; + description + "ATM VCI End Point."; + } + identity opticalChannel { + base iana-interface-type; + description + "Optical Channel."; + } + identity opticalTransport { + base iana-interface-type; + description + "Optical Transport."; + } + identity propAtm { + base iana-interface-type; + description + "Proprietary ATM."; + } + identity voiceOverCable { + base iana-interface-type; + description + "Voice Over Cable Interface."; + } + identity infiniband { + base iana-interface-type; + description + "Infiniband."; + } + identity teLink { + base iana-interface-type; + description + "TE Link."; + } + identity q2931 { + base iana-interface-type; + description + "Q.2931."; + } + identity virtualTg { + base iana-interface-type; + description + "Virtual Trunk Group."; + } + identity sipTg { + base iana-interface-type; + description + "SIP Trunk Group."; + } + identity sipSig { + base iana-interface-type; + description + "SIP Signaling."; + } + identity docsCableUpstreamChannel { + base iana-interface-type; + description + "CATV Upstream Channel."; + } + identity econet { + base iana-interface-type; + description + "Acorn Econet."; + } + identity pon155 { + base iana-interface-type; + description + "FSAN 155Mb Symetrical PON interface."; + } + identity pon622 { + base iana-interface-type; + description + "FSAN 622Mb Symetrical PON interface."; + } + identity bridge { + base iana-interface-type; + description + "Transparent bridge interface."; + } + identity linegroup { + base iana-interface-type; + description + "Interface common to multiple lines."; + } + identity voiceEMFGD { + base iana-interface-type; + description + "Voice E&M Feature Group D."; + } + identity voiceFGDEANA { + base iana-interface-type; + description + "Voice FGD Exchange Access North American."; + } + identity voiceDID { + base iana-interface-type; + description + "Voice Direct Inward Dialing."; + } + identity mpegTransport { + base iana-interface-type; + description + "MPEG transport interface."; + } + identity sixToFour { + base iana-interface-type; + status deprecated; + description + "6to4 interface (DEPRECATED)."; + reference + "RFC 4087 - IP Tunnel MIB"; + } + identity gtp { + base iana-interface-type; + description + "GTP (GPRS Tunneling Protocol)."; + } + identity pdnEtherLoop1 { + base iana-interface-type; + description + "Paradyne EtherLoop 1."; + } + identity pdnEtherLoop2 { + base iana-interface-type; + description + "Paradyne EtherLoop 2."; + } + identity opticalChannelGroup { + base iana-interface-type; + description + "Optical Channel Group."; + } + identity homepna { + base iana-interface-type; + description + "HomePNA ITU-T G.989."; + } + identity gfp { + base iana-interface-type; + description + "Generic Framing Procedure (GFP)."; + } + identity ciscoISLvlan { + base iana-interface-type; + description + "Layer 2 Virtual LAN using Cisco ISL."; + } + identity actelisMetaLOOP { + base iana-interface-type; + description + "Acteleis proprietary MetaLOOP High Speed Link."; + } + identity fcipLink { + base iana-interface-type; + description + "FCIP Link."; + } + identity rpr { + base iana-interface-type; + description + "Resilient Packet Ring Interface Type."; + } + identity qam { + base iana-interface-type; + description + "RF Qam Interface."; + } + identity lmp { + base iana-interface-type; + description + "Link Management Protocol."; + reference + "RFC 4327 - Link Management Protocol (LMP) Management + Information Base (MIB)"; + } + identity cblVectaStar { + base iana-interface-type; + description + "Cambridge Broadband Networks Limited VectaStar."; + } + identity docsCableMCmtsDownstream { + base iana-interface-type; + description + "CATV Modular CMTS Downstream Interface."; + } + identity adsl2 { + base iana-interface-type; + status deprecated; + description + "Asymmetric Digital Subscriber Loop Version 2 + (DEPRECATED/OBSOLETED - please use adsl2plus(238) + instead)."; + reference + "RFC 4706 - Definitions of Managed Objects for Asymmetric + Digital Subscriber Line 2 (ADSL2)"; + } + identity macSecControlledIF { + base iana-interface-type; + description + "MACSecControlled."; + } + identity macSecUncontrolledIF { + base iana-interface-type; + description + "MACSecUncontrolled."; + } + identity aviciOpticalEther { + base iana-interface-type; + description + "Avici Optical Ethernet Aggregate."; + } + identity atmbond { + base iana-interface-type; + description + "atmbond."; + } + identity voiceFGDOS { + base iana-interface-type; + description + "Voice FGD Operator Services."; + } + identity mocaVersion1 { + base iana-interface-type; + description + "MultiMedia over Coax Alliance (MoCA) Interface + as documented in information provided privately to IANA."; + } + identity ieee80216WMAN { + base iana-interface-type; + description + "IEEE 802.16 WMAN interface."; + } + identity adsl2plus { + base iana-interface-type; + description + "Asymmetric Digital Subscriber Loop Version 2 - + Version 2 Plus and all variants."; + } + identity dvbRcsMacLayer { + base iana-interface-type; + description + "DVB-RCS MAC Layer."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity dvbTdm { + base iana-interface-type; + description + "DVB Satellite TDM."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity dvbRcsTdma { + base iana-interface-type; + description + "DVB-RCS TDMA."; + reference + "RFC 5728 - The SatLabs Group DVB-RCS MIB"; + } + identity x86Laps { + base iana-interface-type; + description + "LAPS based on ITU-T X.86/Y.1323."; + } + identity wwanPP { + base iana-interface-type; + description + "3GPP WWAN."; + } + identity wwanPP2 { + base iana-interface-type; + description + "3GPP2 WWAN."; + } + identity voiceEBS { + base iana-interface-type; + description + "Voice P-phone EBS physical interface."; + } + identity ifPwType { + base iana-interface-type; + description + "Pseudowire interface type."; + reference + "RFC 5601 - Pseudowire (PW) Management Information Base (MIB)"; + } + identity ilan { + base iana-interface-type; + description + "Internal LAN on a bridge per IEEE 802.1ap."; + } + identity pip { + base iana-interface-type; + description + "Provider Instance Port on a bridge per IEEE 802.1ah PBB."; + } + identity aluELP { + base iana-interface-type; + description + "Alcatel-Lucent Ethernet Link Protection."; + } + identity gpon { + base iana-interface-type; + description + "Gigabit-capable passive optical networks (G-PON) as per + ITU-T G.948."; + } + identity vdsl2 { + base iana-interface-type; + description + "Very high speed digital subscriber line Version 2 + (as per ITU-T Recommendation G.993.2)."; + reference + "RFC 5650 - Definitions of Managed Objects for Very High + Speed Digital Subscriber Line 2 (VDSL2)"; + } + identity capwapDot11Profile { + base iana-interface-type; + description + "WLAN Profile Interface."; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + identity capwapDot11Bss { + base iana-interface-type; + description + "WLAN BSS Interface."; + reference + "RFC 5834 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Binding MIB for + IEEE 802.11"; + } + identity capwapWtpVirtualRadio { + base iana-interface-type; + description + "WTP Virtual Radio Interface."; + reference + "RFC 5833 - Control and Provisioning of Wireless Access + Points (CAPWAP) Protocol Base MIB"; + } + identity bits { + base iana-interface-type; + description + "bitsport."; + } + identity docsCableUpstreamRfPort { + base iana-interface-type; + description + "DOCSIS CATV Upstream RF Port."; + } + identity cableDownstreamRfPort { + base iana-interface-type; + description + "CATV downstream RF Port."; + } + identity vmwareVirtualNic { + base iana-interface-type; + description + "VMware Virtual Network Interface."; + } + identity ieee802154 { + base iana-interface-type; + description + "IEEE 802.15.4 WPAN interface."; + reference + "IEEE 802.15.4-2006"; + } + identity otnOdu { + base iana-interface-type; + description + "OTN Optical Data Unit."; + } + identity otnOtu { + base iana-interface-type; + description + "OTN Optical channel Transport Unit."; + } + identity ifVfiType { + base iana-interface-type; + description + "VPLS Forwarding Instance Interface Type."; + } + identity g9981 { + base iana-interface-type; + description + "G.998.1 bonded interface."; + } + identity g9982 { + base iana-interface-type; + description + "G.998.2 bonded interface."; + } + identity g9983 { + base iana-interface-type; + description + "G.998.3 bonded interface."; + } + + identity aluEpon { + base iana-interface-type; + description + "Ethernet Passive Optical Networks (E-PON)."; + } + identity aluEponOnu { + base iana-interface-type; + description + "EPON Optical Network Unit."; + } + identity aluEponPhysicalUni { + base iana-interface-type; + description + "EPON physical User to Network interface."; + } + identity aluEponLogicalLink { + base iana-interface-type; + description + "The emulation of a point-to-point link over the EPON + layer."; + } + identity aluGponOnu { + base iana-interface-type; + description + "GPON Optical Network Unit."; + reference + "ITU-T G.984.2"; + } + identity aluGponPhysicalUni { + base iana-interface-type; + description + "GPON physical User to Network interface."; + reference + "ITU-T G.984.2"; + } + identity vmwareNicTeam { + base iana-interface-type; + description + "VMware NIC Team."; + } + identity docsOfdmDownstream { + base iana-interface-type; + description + "CATV Downstream OFDM interface."; + } + identity docsOfdmaUpstream { + base iana-interface-type; + description + "CATV Upstream OFDMA interface."; + } + identity gfast { + base iana-interface-type; + description + "G.fast port."; + reference + "ITU-T G.9701"; + } + identity sdci { + base iana-interface-type; + description + "SDCI (IO-Link)."; + reference + "IEC 61131-9 Edition 1.0 2013-09"; + } +} diff --git a/models/yang/common/ietf-inet-types.yang b/models/yang/common/ietf-inet-types.yang new file mode 100644 index 0000000000..2f14270dec --- /dev/null +++ b/models/yang/common/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/common/ietf-interfaces.yang b/models/yang/common/ietf-interfaces.yang new file mode 100644 index 0000000000..ff2586abe9 --- /dev/null +++ b/models/yang/common/ietf-interfaces.yang @@ -0,0 +1,1121 @@ +module ietf-interfaces { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-interfaces"; + prefix if; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (Network Modeling) Working Group"; + + contact + "WG Web: + WG List: + + Editor: Martin Bjorklund + "; + + description + "This module contains a collection of YANG definitions for + managing network interfaces. + + Copyright (c) 2018 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 + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 8343; see + the RFC itself for full legal notices."; + + revision 2018-02-20 { + description + "Updated to support NMDA."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + + revision 2014-05-08 { + description + "Initial revision."; + reference + "RFC 7223: A YANG Data Model for Interface Management"; + } + + /* + * Typedefs + */ + + typedef interface-ref { + type leafref { + path "/if:interfaces/if:interface/if:name"; + } + description + "This type is used by data models that need to reference + interfaces."; + } + + /* + * Identities + */ + + identity interface-type { + description + "Base identity from which specific interface types are + derived."; + } + + /* + * Features + */ + + feature arbitrary-names { + description + "This feature indicates that the device allows user-controlled + interfaces to be named arbitrarily."; + } + feature pre-provisioning { + description + "This feature indicates that the device supports + pre-provisioning of interface configuration, i.e., it is + possible to configure an interface whose physical interface + hardware is not present on the device."; + } + feature if-mib { + description + "This feature indicates that the device implements + the IF-MIB."; + reference + "RFC 2863: The Interfaces Group MIB"; + } + + /* + * Data nodes + */ + + container interfaces { + description + "Interface parameters."; + + list interface { + key "name"; + + description + "The list of interfaces on the device. + + The status of an interface is available in this list in the + operational state. If the configuration of a + system-controlled interface cannot be used by the system + (e.g., the interface hardware present does not match the + interface type), then the configuration is not applied to + the system-controlled interface shown in the operational + state. If the configuration of a user-controlled interface + cannot be used by the system, the configured interface is + not instantiated in the operational state. + + System-controlled interfaces created by the system are + always present in this list in the operational state, + whether or not they are configured."; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + operational state, the server MAY reject the request if + the implementation does not support pre-provisioning of + interfaces or if the name refers to an interface that can + never exist in the system. A Network Configuration + Protocol (NETCONF) server MUST reply with an rpc-error + with the error-tag 'invalid-value' in this case. + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + operational state. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + configuration."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the intended configuration to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the intended configuration are + reflected in ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf link-up-down-trap-enable { + if-feature if-mib; + type enumeration { + enum enabled { + value 1; + description + "The device will generate linkUp/linkDown SNMP + notifications for this interface."; + } + enum disabled { + value 2; + description + "The device will not generate linkUp/linkDown SNMP + notifications for this interface."; + } + } + description + "Controls whether linkUp/linkDown SNMP notifications + should be generated for this interface. + + If this node is not configured, the value 'enabled' is + operationally used by the server for interfaces that do + not operate on top of any other interface (i.e., there are + no 'lower-layer-if' entries), and 'disabled' otherwise."; + reference + "RFC 2863: The Interfaces Group MIB - + ifLinkUpDownTrapEnable"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + config false; + mandatory true; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + config false; + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + config false; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + config false; + mandatory true; + description + "The ifIndex value for the ifEntry represented by this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + config false; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-ref; + config false; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-ref; + config false; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + config false; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + config false; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + + } + } + + /* + * Legacy typedefs + */ + + typedef interface-state-ref { + type leafref { + path "/if:interfaces-state/if:interface/if:name"; + } + status deprecated; + description + "This type is used by data models that need to reference + the operationally present interfaces."; + } + + /* + * Legacy operational state data nodes + */ + + container interfaces-state { + config false; + status deprecated; + description + "Data nodes for the operational state of interfaces."; + + list interface { + key "name"; + status deprecated; + + description + "The list of interfaces on the device. + + System-controlled interfaces created by the system are + always present in this list, whether or not they are + configured."; + + leaf name { + type string; + status deprecated; + description + "The name of the interface. + + A server implementation MAY map this leaf to the ifName + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifName. The definition of + such a mechanism is outside the scope of this document."; + reference + "RFC 2863: The Interfaces Group MIB - ifName"; + } + + leaf type { + type identityref { + base interface-type; + } + mandatory true; + status deprecated; + description + "The type of the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf admin-status { + if-feature if-mib; + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "Not ready to pass packets and not in some test mode."; + } + enum testing { + value 3; + description + "In some test mode."; + } + } + mandatory true; + status deprecated; + description + "The desired state of the interface. + + This leaf has the same read semantics as ifAdminStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + leaf oper-status { + type enumeration { + enum up { + value 1; + description + "Ready to pass packets."; + } + enum down { + value 2; + description + "The interface does not pass any packets."; + } + enum testing { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum unknown { + value 4; + description + "Status cannot be determined for some reason."; + } + enum dormant { + value 5; + description + "Waiting for some external event."; + } + enum not-present { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum lower-layer-down { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + mandatory true; + status deprecated; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + } + + leaf last-change { + type yang:date-and-time; + status deprecated; + description + "The time the interface entered its current operational + state. If the current state was entered prior to the + last re-initialization of the local network management + subsystem, then this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifLastChange"; + } + + leaf if-index { + if-feature if-mib; + type int32 { + range "1..2147483647"; + } + mandatory true; + status deprecated; + description + "The ifIndex value for the ifEntry represented by this + interface."; + + reference + "RFC 2863: The Interfaces Group MIB - ifIndex"; + } + + leaf phys-address { + type yang:phys-address; + status deprecated; + description + "The interface's address at its protocol sub-layer. For + example, for an 802.x interface, this object normally + contains a Media Access Control (MAC) address. The + interface's media-specific modules must define the bit + and byte ordering and the format of the value of this + object. For interfaces that do not have such an address + (e.g., a serial line), this node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - ifPhysAddress"; + } + + leaf-list higher-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered on top of this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf-list lower-layer-if { + type interface-state-ref; + status deprecated; + description + "A list of references to interfaces layered underneath this + interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifStackTable"; + } + + leaf speed { + type yang:gauge64; + units "bits/second"; + status deprecated; + description + "An estimate of the interface's current bandwidth in bits + per second. For interfaces that do not vary in + bandwidth or for those where no accurate estimation can + + be made, this node should contain the nominal bandwidth. + For interfaces that have no concept of bandwidth, this + node is not present."; + reference + "RFC 2863: The Interfaces Group MIB - + ifSpeed, ifHighSpeed"; + } + + container statistics { + status deprecated; + description + "A collection of interface-related statistics objects."; + + leaf discontinuity-time { + type yang:date-and-time; + mandatory true; + status deprecated; + description + "The time on the most recent occasion at which any one or + more of this interface's counters suffered a + discontinuity. If no such discontinuities have occurred + since the last re-initialization of the local management + subsystem, then this node contains the time the local + management subsystem re-initialized itself."; + } + + leaf in-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type yang:counter32; + status deprecated; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf out-octets { + type yang:counter64; + status deprecated; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-unicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + leaf out-multicast-pkts { + type yang:counter64; + status deprecated; + description + "The total number of packets that higher-level protocols + requested be transmitted and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type yang:counter32; + status deprecated; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type yang:counter32; + status deprecated; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system and at + other times as indicated by the value of + 'discontinuity-time'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + } + } + } +} diff --git a/models/yang/common/ietf-yang-types.yang b/models/yang/common/ietf-yang-types.yang new file mode 100644 index 0000000000..ee58fa3ab0 --- /dev/null +++ b/models/yang/common/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/common/openconfig-aaa-radius.yang b/models/yang/common/openconfig-aaa-radius.yang new file mode 100644 index 0000000000..a18b9d68d2 --- /dev/null +++ b/models/yang/common/openconfig-aaa-radius.yang @@ -0,0 +1,186 @@ +submodule openconfig-aaa-radius { + + yang-version "1"; + + belongs-to "openconfig-aaa" { + prefix "oc-aaa"; + } + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-aaa-types { prefix oc-aaa-types; } + import openconfig-types { prefix oc-types; } + import openconfig-yang-types { prefix oc-yang; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + related to the RADIUS protocol for authentication, + authorization, and accounting."; + + oc-ext:openconfig-version "0.4.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.1"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // extension statements + + // feature statements + + // identity statements + + identity RADIUS { + base oc-aaa-types:AAA_SERVER_TYPE; + description + "Remote Authentication Dial In User Service (RADIUS) AAA + server"; + reference + "RFC 2865 - Remote Authentication Dial In User Service + (RADIUS)"; + } + + // typedef statements + + // grouping statements + + grouping aaa-radius-server-config { + description + "Configuration data for a RADIUS server"; + + leaf auth-port { + type oc-inet:port-number; + default 1812; + description + "Port number for authentication requests"; + } + + leaf acct-port { + type oc-inet:port-number; + default 1813; + description + "Port number for accounting requests"; + } + + leaf secret-key { + type oc-types:routing-password; + description + "The unencrypted shared key used between the authentication + server and the device."; + } + + leaf source-address { + type oc-inet:ip-address; + description + "Source IP address to use in messages to the RADIUS server"; + } + + leaf retransmit-attempts { + type uint8; + description + "Number of times the system may resend a request to the + RADIUS server when it is unresponsive"; + } + } + + grouping aaa-radius-server-state { + description + "Operational state data for a RADIUS server"; + + container counters { + description + "A collection of RADIUS related state objects."; + + leaf retried-access-requests { + type oc-yang:counter64; + description + "Retransmitted Access-Request messages."; + } + + leaf access-accepts { + type oc-yang:counter64; + description + "Received Access-Accept messages."; + } + + leaf access-rejects { + type oc-yang:counter64; + description + "Received Access-Reject messages."; + } + + leaf timeout-access-requests { + type oc-yang:counter64; + description + "Access-Request messages that have timed-out, + requiring retransmission."; + } + } + } + + grouping aaa-radius-server-top { + description + "Top-level grouping for RADIUS server data"; + + container radius { + description + "Top-level container for RADIUS server data"; + + container config { + description + "Configuration data for RADIUS servers"; + + uses aaa-radius-server-config; + } + + container state { + + config false; + + description + "Operational state data for RADIUS servers"; + + uses aaa-radius-server-config; + uses aaa-radius-server-state; + } + } + } + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-aaa-tacacs.yang b/models/yang/common/openconfig-aaa-tacacs.yang new file mode 100644 index 0000000000..1320bd0cf5 --- /dev/null +++ b/models/yang/common/openconfig-aaa-tacacs.yang @@ -0,0 +1,142 @@ +submodule openconfig-aaa-tacacs { + + yang-version "1"; + + belongs-to "openconfig-aaa" { + prefix "oc-aaa"; + } + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-aaa-types { prefix oc-aaa-types; } + import openconfig-types { prefix oc-types; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + related to the TACACS+ protocol for authentication, + authorization, and accounting."; + + oc-ext:openconfig-version "0.4.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.1"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // extension statements + + // feature statements + + // identity statements + + identity TACACS { + base oc-aaa-types:AAA_SERVER_TYPE; + description + "Terminal Access Controller Access Control System (TACACS+) + AAA server"; + reference + "The TACACS+ Protocol (draft-ietf-opsawg-tacacs-05) + RFC 1492 - An Access Control Protocol, Sometimes Called + TACACS"; + } + + // typedef statements + + // grouping statements + + grouping aaa-tacacs-server-config { + description + "Configuration data for a TACACS+ server"; + + leaf port { + type oc-inet:port-number; + default 49; + description + "The port number on which to contact the TACACS server"; + } + + leaf secret-key { + type oc-types:routing-password; + description + "The unencrypted shared key used between the authentication + server and the device."; + } + + leaf source-address { + type oc-inet:ip-address; + description + "Source IP address to use in messages to the TACACS server"; + } + } + + grouping aaa-tacacs-server-state { + description + "Operational state data for a TACACS+ server"; + } + + grouping aaa-tacacs-server-top { + description + "Top-level grouping for TACACS+ sever data"; + + container tacacs { + description + "Top-level container for TACACS+ server data"; + + container config { + description + "Configuration data for TACACS+ server"; + + uses aaa-tacacs-server-config; + } + + container state { + + config false; + + description + "Operational state data for TACACS+ server"; + + uses aaa-tacacs-server-config; + uses aaa-tacacs-server-state; + } + } + } + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-aaa-types.yang b/models/yang/common/openconfig-aaa-types.yang new file mode 100644 index 0000000000..8385eca79e --- /dev/null +++ b/models/yang/common/openconfig-aaa-types.yang @@ -0,0 +1,172 @@ +module openconfig-aaa-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/aaa/types"; + + prefix "oc-aaa-types"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines shared types for data related to AAA + (authentication, authorization, accounting)."; + + oc-ext:openconfig-version "0.4.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.1"; + } + + revision "2018-04-12" { + description + "Add when conditions, correct identities"; + reference "0.4.0"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity AAA_SERVER_TYPE { + description + "Base identity for types of AAA servers"; + } + + + identity SYSTEM_DEFINED_ROLES { + description + "Base identity for system_defined roles that can be assigned + to users."; + } + + identity SYSTEM_ROLE_ADMIN { + base SYSTEM_DEFINED_ROLES; + description + "Built-in role that allows the equivalent of superuser + permission for all configuration and operational commands + on the device."; + } + + identity AAA_ACCOUNTING_EVENT_TYPE { + description + "Base identity for specifying events types that should be + sent to AAA server for accounting"; + } + + identity AAA_ACCOUNTING_EVENT_COMMAND { + base AAA_ACCOUNTING_EVENT_TYPE; + description + "Specifies interactive command events for AAA accounting"; + } + + identity AAA_ACCOUNTING_EVENT_LOGIN { + base AAA_ACCOUNTING_EVENT_TYPE; + description + "Specifies login events for AAA accounting"; + } + + identity AAA_AUTHORIZATION_EVENT_TYPE { + description + "Base identity for specifying activities that should be + sent to AAA server for authorization"; + } + + identity AAA_AUTHORIZATION_EVENT_COMMAND { + base AAA_AUTHORIZATION_EVENT_TYPE; + description + "Specifies interactive command events for AAA authorization"; + } + + identity AAA_AUTHORIZATION_EVENT_CONFIG { + base AAA_AUTHORIZATION_EVENT_TYPE; + description + "Specifies configuration (e.g., EXEC) events for AAA + authorization"; + } + + identity AAA_METHOD_TYPE { + description + "Base identity to define well-known methods for AAA + operations"; + } + + identity TACACS_ALL { + base AAA_METHOD_TYPE; + description + "The group of all TACACS+ servers."; + } + + identity RADIUS_ALL { + base AAA_METHOD_TYPE; + description + "The group of all RADIUS servers."; + } + + identity LOCAL { + base AAA_METHOD_TYPE; + description + "Locally configured method for AAA operations."; + } + + + // typedef statements + + typedef crypt-password-type { + type string; + description + "A password that is hashed based on the hash algorithm + indicated by the prefix in the string. The string + takes the following form, based on the Unix crypt function: + + $[$=(,=)*][$[$]] + + Common hash functions include: + + id | hash function + ---+--------------- + 1 | MD5 + 2a| Blowfish + 2y| Blowfish (correct handling of 8-bit chars) + 5 | SHA-256 + 6 | SHA-512 + + These may not all be supported by a target device."; + } + + +} diff --git a/models/yang/common/openconfig-aaa.yang b/models/yang/common/openconfig-aaa.yang new file mode 100644 index 0000000000..7a2fffa003 --- /dev/null +++ b/models/yang/common/openconfig-aaa.yang @@ -0,0 +1,822 @@ +module openconfig-aaa { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/aaa"; + + prefix "oc-aaa"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + import openconfig-inet-types { prefix oc-inet; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-aaa-types { prefix oc-aaa-types; } + + include openconfig-aaa-tacacs; + include openconfig-aaa-radius; + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + related to authorization, authentication, and accounting (AAA) + management. + + Portions of this model reuse data definitions or structure from + RFC 7317 - A YANG Data Model for System Management"; + + oc-ext:openconfig-version "0.4.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.1"; + } + + revision "2018-04-12" { + description + "Add when conditions, correct identities"; + reference "0.4.0"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + // grouping statements + grouping aaa-servergroup-common-config { + description + "Configuration data for AAA server groups"; + + leaf name { + type string; + description + "Name for the server group"; + } + + leaf type { + type identityref { + base oc-aaa-types:AAA_SERVER_TYPE; + } + description + "AAA server type -- all servers in the group must be of this + type"; + } + } + + grouping aaa-servergroup-common-state { + description + "Operational state data for AAA server groups"; + + //TODO: add list of group members as opstate + } + + grouping aaa-servergroup-common-top { + description + "Top-level grouping for AAA server groups"; + + container server-groups { + description + "Enclosing container for AAA server groups"; + + list server-group { + key "name"; + description + "List of AAA server groups. All servers in a group + must have the same type as indicated by the server + type."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to configured name of the server group"; + } + + container config { + description + "Configuration data for each server group"; + + uses aaa-servergroup-common-config; + } + + container state { + config false; + + description + "Operational state data for each server group"; + + uses aaa-servergroup-common-config; + uses aaa-servergroup-common-state; + } + + uses aaa-server-top; + } + } + } + + grouping aaa-server-config { + description + "Common configuration data for AAA servers"; + + leaf name { + type string; + description + "Name assigned to the server"; + } + + + leaf address { + type oc-inet:ip-address; + description "Address of the authentication server"; + } + + leaf timeout { + type uint16; + units seconds; + description + "Set the timeout in seconds on responses from the AAA + server"; + } + } + + grouping aaa-server-state { + description + "Common operational state data for AAA servers"; + + leaf connection-opens { + type oc-yang:counter64; + description + "Number of new connection requests sent to the server, e.g. + socket open"; + } + + leaf connection-closes { + type oc-yang:counter64; + description + "Number of connection close requests sent to the server, e.g. + socket close"; + } + + leaf connection-aborts { + type oc-yang:counter64; + description + "Number of aborted connections to the server. These do + not include connections that are close gracefully."; + } + + leaf connection-failures { + type oc-yang:counter64; + description + "Number of connection failures to the server"; + } + + leaf connection-timeouts { + type oc-yang:counter64; + description + "Number of connection timeouts to the server"; + } + + leaf messages-sent { + type oc-yang:counter64; + description + "Number of messages sent to the server"; + } + + leaf messages-received { + type oc-yang:counter64; + description + "Number of messages received by the server"; + } + + leaf errors-received { + type oc-yang:counter64; + description + "Number of error messages received from the server"; + } + + } + + grouping aaa-server-top { + description + "Top-level grouping for list of AAA servers"; + + container servers { + description + "Enclosing container the list of servers"; + + list server { + key "address"; + description + "List of AAA servers"; + + leaf address { + type leafref { + path "../config/address"; + } + description + "Reference to the configured address of the AAA server"; + } + + container config { + description + "Configuration data "; + + uses aaa-server-config; + } + + container state { + config false; + + description + "Operational state data "; + + uses aaa-server-config; + uses aaa-server-state; + } + + uses aaa-tacacs-server-top { + when "../../config/type = 'oc-aaa-types:TACACS'"; + } + + uses aaa-radius-server-top { + when "../../config/type = 'oc-aaa-types:RADIUS'"; + } + } + } + } + + grouping aaa-admin-config { + description + "Configuration data for the system built-in + administrator / root user account"; + + leaf admin-password { + type string; + oc-ext:openconfig-hashed-value; + description + "The admin/root password, supplied as a cleartext string. + The system should hash and only store the password as a + hashed value."; + } + + leaf admin-password-hashed { + type oc-aaa-types:crypt-password-type; + description + "The admin/root password, supplied as a hashed value + using the notation described in the definition of the + crypt-password-type."; + } + } + + grouping aaa-admin-state { + description + "Operational state data for the root user"; + + leaf admin-username { + type string; + description + "Name of the administrator user account, e.g., admin, root, + etc."; + } + } + + grouping aaa-authentication-admin-top { + description + "Top-level grouping for root user configuration and state + data"; + + container admin-user { + description + "Top-level container for the system root or admin user + configuration and operational state"; + + container config { + description + "Configuration data for the root user account"; + + uses aaa-admin-config; + } + + container state { + config false; + + description + "Operational state data for the root user account"; + + uses aaa-admin-config; + uses aaa-admin-state; + } + } + } + grouping aaa-authentication-user-config { + description + "Configuration data for local users"; + + leaf username { + type string; + description + "Assigned username for this user"; + } + + leaf password { + type string; + oc-ext:openconfig-hashed-value; + description + "The user password, supplied as cleartext. The system + must hash the value and only store the hashed value."; + } + + leaf password-hashed { + type oc-aaa-types:crypt-password-type; + description + "The user password, supplied as a hashed value + using the notation described in the definition of the + crypt-password-type."; + } + + leaf ssh-key { + type string; + description + "SSH public key for the user (RSA or DSA)"; + } + + leaf role { + type union { + type string; + type identityref { + base oc-aaa-types:SYSTEM_DEFINED_ROLES; + } + } + description + "Role assigned to the user. The role may be supplied + as a string or a role defined by the SYSTEM_DEFINED_ROLES + identity."; + } + } + + grouping aaa-authentication-user-state { + description + "Operational state data for local users"; + } + + grouping aaa-authentication-user-top { + description + "Top-level grouping for local users"; + + container users { + description + "Enclosing container list of local users"; + + list user { + key "username"; + description + "List of local users on the system"; + + leaf username { + type leafref { + path "../config/username"; + } + description + "References the configured username for the user"; + } + + container config { + description + "Configuration data for local users"; + + uses aaa-authentication-user-config; + } + + container state { + config false; + + description + "Operational state data for local users"; + + uses aaa-authentication-user-config; + uses aaa-authentication-user-state; + } + } + + } + } + + grouping aaa-accounting-methods-common { + description + "Common definitions for accounting methods"; + + leaf-list accounting-method { + type union { + type identityref { + base oc-aaa-types:AAA_METHOD_TYPE; + } + type string; + //TODO: in YANG 1.1 this should be converted to a leafref to + //point to the server group name. + } + ordered-by user; + description + "An ordered list of methods used for AAA accounting for this + event type. The method is defined by the destination for + accounting data, which may be specified as the group of + all TACACS+/RADIUS servers, a defined server group, or + the local system."; + } + } + + + grouping aaa-accounting-events-config { + description + "Configuration data for AAA accounting events"; + + leaf event-type { + type identityref { + base oc-aaa-types:AAA_ACCOUNTING_EVENT_TYPE; + } + description + "The type of activity to record at the AAA accounting + server"; + } + + leaf record { + type enumeration { + enum START_STOP { + description + "Send START record to the accounting server at the + beginning of the activity, and STOP record at the + end of the activity."; + } + enum STOP { + description + "Send STOP record to the accounting server when the + user activity completes"; + } + } + description + "Type of record to send to the accounting server for this + activity type"; + } + } + + grouping aaa-accounting-events-state { + description + "Operational state data for accounting events"; + } + + grouping aaa-accounting-events-top { + description + "Top-level grouping for accounting events"; + + container events { + description + "Enclosing container for defining handling of events + for accounting"; + + list event { + key "event-type"; + description + "List of events subject to accounting"; + + leaf event-type { + type leafref { + path "../config/event-type"; + } + description + "Reference to the event-type being logged at the + accounting server"; + } + + container config { + description + "Configuration data for accounting events"; + + uses aaa-accounting-events-config; + } + + container state { + config false; + + description + "Operational state data for accounting events"; + + uses aaa-accounting-events-config; + uses aaa-accounting-events-state; + } + } + } + } + + grouping aaa-accounting-config { + description + "Configuration data for event accounting"; + + uses aaa-accounting-methods-common; + + } + + grouping aaa-accounting-state { + description + "Operational state data for event accounting services"; + } + + grouping aaa-accounting-top { + description + "Top-level grouping for user activity accounting"; + + container accounting { + description + "Top-level container for AAA accounting"; + + container config { + description + "Configuration data for user activity accounting."; + + uses aaa-accounting-config; + } + + container state { + config false; + + description + "Operational state data for user accounting."; + + uses aaa-accounting-config; + uses aaa-accounting-state; + } + + uses aaa-accounting-events-top; + + } + } + + grouping aaa-authorization-methods-config { + description + "Common definitions for authorization methods for global + and per-event type"; + + leaf-list authorization-method { + type union { + type identityref { + base oc-aaa-types:AAA_METHOD_TYPE; + } + type string; + } + ordered-by user; + description + "Ordered list of methods for authorizing commands. The first + method that provides a response (positive or negative) should + be used. The list may contain a well-defined method such + as the set of all TACACS or RADIUS servers, or the name of + a defined AAA server group. The system must validate + that the named server group exists."; + } + } + + grouping aaa-authorization-events-config { + description + "Configuration data for AAA authorization events"; + + leaf event-type { + type identityref { + base oc-aaa-types:AAA_AUTHORIZATION_EVENT_TYPE; + } + description + "The type of event to record at the AAA authorization + server"; + } + } + + grouping aaa-authorization-events-state { + description + "Operational state data for AAA authorization events"; + } + + grouping aaa-authorization-events-top { + description + "Top-level grouping for authorization events"; + + container events { + description + "Enclosing container for the set of events subject + to authorization"; + + list event { + key "event-type"; + description + "List of events subject to AAA authorization"; + + leaf event-type { + type leafref { + path "../config/event-type"; + } + description + "Reference to the event-type list key"; + } + + container config { + description + "Configuration data for each authorized event"; + + uses aaa-authorization-events-config; + } + + container state { + config false; + + description + "Operational state data for each authorized activity"; + + uses aaa-authorization-events-config; + uses aaa-authorization-events-state; + } + } + } + } + + grouping aaa-authorization-config { + description + "Configuration data for AAA authorization"; + + uses aaa-authorization-methods-config; + } + + grouping aaa-authorization-state { + description + "Operational state data for AAA authorization"; + } + + grouping aaa-authorization-top { + description + "Top-level grouping for AAA authorization"; + + container authorization { + description + "Top-level container for AAA authorization configuration + and operational state data"; + + container config { + description + "Configuration data for authorization based on AAA + methods"; + + uses aaa-authorization-config; + } + + container state { + config false; + + description + "Operational state data for authorization based on AAA"; + + uses aaa-authorization-config; + uses aaa-authorization-state; + } + + uses aaa-authorization-events-top; + + } + } + + grouping aaa-authentication-config { + description + "Configuration data for global authentication"; + + leaf-list authentication-method { + type union { + type identityref { + base oc-aaa-types:AAA_METHOD_TYPE; + } + type string; + //TODO: string should be a leafref to a defined + //server group. this will be possible in YANG 1.1 + //type leafref { + //path "/aaa/server-groups/server-group/config/name"; + //} + } + ordered-by user; + description + "Ordered list of authentication methods for users. This + can be either a reference to a server group, or a well- + defined designation in the AAA_METHOD_TYPE identity. If + authentication fails with one method, the next defined + method is tried -- failure of all methods results in the + user being denied access."; + } + } + + grouping aaa-authentication-state { + description + "Operational state data for global authentication"; + } + + grouping aaa-authentication-top { + description + "Top-level grouping for top-level authentication"; + + container authentication { + description + "Top-level container for global authentication data"; + + container config { + description + "Configuration data for global authentication services"; + + uses aaa-authentication-config; + } + + container state { + config false; + + description + "Operational state data for global authentication + services"; + + uses aaa-authentication-config; + uses aaa-authentication-state; + } + + uses aaa-authentication-admin-top; + uses aaa-authentication-user-top; + } + } + + grouping aaa-config { + description + "Configuration data for top level AAA"; + } + + grouping aaa-state { + description + "Operational state data for top level AAA"; + } + + grouping aaa-top { + description + "Top-level grouping for AAA services"; + + container aaa { + description + "Top-level container for AAA services"; + + container config { + description + "Configuration data for top level AAA services"; + + uses aaa-config; + } + + container state { + config false; + + description + "Operational state data for top level AAA services "; + + uses aaa-config; + uses aaa-state; + } + + uses aaa-authentication-top; + uses aaa-authorization-top; + uses aaa-accounting-top; + uses aaa-servergroup-common-top; + + } + } + + + + // data definition statements + + +} diff --git a/models/yang/common/openconfig-aft-common.yang b/models/yang/common/openconfig-aft-common.yang new file mode 100644 index 0000000000..7c94e5c07f --- /dev/null +++ b/models/yang/common/openconfig-aft-common.yang @@ -0,0 +1,391 @@ +submodule openconfig-aft-common { + belongs-to "openconfig-aft" { + prefix "oc-aft"; + } + + import openconfig-interfaces { prefix "oc-if"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-yang-types { prefix "oc-yang"; } + import openconfig-inet-types { prefix "oc-inet"; } + import openconfig-mpls-types { prefix "oc-mplst"; } + import openconfig-policy-types { prefix "oc-pol-types"; } + import openconfig-aft-types { prefix "oc-aftt"; } + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Submodule containing definitions of groupings that are re-used + across multiple contexts within the AFT model."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + grouping aft-nhop-structural { + description + "Structural grouping describing a next-hop entry."; + + container next-hops { + description + "The list of next-hops that are to be used for entry within + the AFT table. The structure of each next-hop is address + family independent, such that it is possible to resolve fully + how the next-hop is treated. For example: + + - Where ingress IPv4 unicast packets are to be forwarded via + an MPLS LSP, the next-hop list should indicate the MPLS + label stack that is used to the next-hop. + - Where ingress MPLS labelled packets are to be forwarded to + an IPv6 nexthop (for example, a CE within a VPN, then the + popped label stack, and IPv6 next-hop address should be + indicated)."; + + list next-hop { + key "index"; + + description + "A next-hop associated with the forwarding instance."; + + leaf index { + type leafref { + path "../config/index"; + } + description + "A unique index identifying the next-hop entry for the + AFT entry"; + + } + + container config { + description + "Configuration parameters relating to the AFT next-hop + entry"; + + uses aft-common-entry-nexthop-config; + } + + container state { + config false; + description + "Operational state parameters relating to the AFT + next-hop entry"; + + uses aft-common-entry-nexthop-config; + uses aft-common-entry-nexthop-state; + } + + uses oc-if:interface-ref; + } + } + } + + grouping aft-common-entry-state { + description + "Operational state parameters relating to a forwarding entry"; + + leaf packets-forwarded { + type oc-yang:counter64; + description + "The number of packets which have matched, and been forwarded, + based on the AFT entry."; + } + + leaf octets-forwarded { + type oc-yang:counter64; + description + "The number of octets which have matched, and been forwarded, + based on the AFT entry"; + } + + // We are at $afi/$entry/state/next-hop-group + leaf next-hop-group { + type leafref { + path "../../../../next-hop-groups/next-hop-group/state/id"; + } + description + "A reference to the next-hop-group that is in use for the entry + within the AFT. Traffic is distributed across the set of next-hops + within the next-hop group according to the weight."; + } + } + + grouping aft-common-entry-nexthop-config { + description + "Configuration parameters relating to a next-hop entry for a AFT + entry"; + + leaf index { + type uint64; + description + "A unique entry for the next-hop."; + } + } + + grouping aft-common-entry-nexthop-state { + description + "Parameters relating to a next-hop."; + + leaf ip-address { + type oc-inet:ip-address; + description + "The IP address of the next-hop system."; + } + + leaf mac-address { + type oc-yang:mac-address; + description + "The MAC address of the next-hop if resolved by the local + network instance."; + } + + leaf-list pushed-mpls-label-stack { + type oc-mplst:mpls-label; + ordered-by user; + description + "The MPLS label stack imposed when forwarding packets to the + next-hop + - the stack is encoded as a leaf list whereby the order of the + entries is such that the first entry in the list is the + label at the bottom of the stack to be pushed. + + To this end, a packet which is to forwarded to a device using + a service label of 42, and a transport label of 8072 will be + represented with a label stack list of [42, 8072]. + + The MPLS label stack list is ordered by the user, such that no + system re-ordering of leaves is permitted by the system. + + A swap operation is reflected by entries in the + popped-mpls-label-stack and pushed-mpls-label-stack nodes."; + + } + + leaf encapsulate-header { + type oc-aftt:encapsulation-header-type; + description + "When forwarding a packet to the specified next-hop the local + system performs an encapsulation of the packet - adding the + specified header type."; + } + + leaf origin-protocol { + type identityref { + base "oc-pol-types:INSTALL_PROTOCOL_TYPE"; + } + description + "The protocol from which the AFT entry was learned."; + } + } + + grouping aft-common-ip-state { + description + "Common parameters across IP address families"; + + leaf decapsulate-header { + type oc-aftt:encapsulation-header-type; + description + "When forwarding a packet to the specified next-hop, the local + system performs a decapsulation of the packet - removing the + specified header type. In the case that no next-hop is + specified, the packet header is removed, and a subsequent + forwarding lookup is performed on the packet encapsulated + within the header, matched within the relevant AFT within the + specified network-instance."; + } + } + + grouping aft-next-hop-groups-structural { + description + "Logical grouping for groups of next-hops."; + + container next-hop-groups { + description + "Surrounding container for groups of next-hops."; + + list next-hop-group { + key "id"; + + description + "An individual set of next-hops grouped into a common group. + Each entry within an abstract forwarding table points to a + next-hop-group. Entries in the next-hop-group are forwarded to + according to the weights specified for each next-hop group. + + If an entry within the next-hop group becomes unusable, for + example due to an interface failure, the remaining entries + are used until all entries become unusable - at which point + the backup next-hop-group (if specified) is used."; + + leaf id { + description + "A reference to a unique identifier for the next-hop-group."; + + type leafref { + path "../config/id"; + } + } + + container config { + description + "Configuration parameters related to the next-hop-group."; + uses aft-nhg-config; + } + + container state { + config false; + description + "Operational state parameters relating to next-hop-groups."; + uses aft-nhg-config; + uses aft-nhg-state; + } + + container next-hops { + description + "Surrounding container for the list of next-hops within + the next-hop-group."; + + list next-hop { + key "index"; + + description + "An individual next-hop within the next-hop-group. Each + next-hop is a reference to an entry within the next-hop + list."; + + leaf index { + description + "A reference to the index for the next-hop within the + the next-hop-group."; + type leafref { + path "../config/index"; + } + } + + container config { + description + "Configuration parameters related to a next-hop within + the next-hop-group."; + uses aft-nhg-nh-config; + } + + container state { + config false; + description + "Operational state parameters related to a next-hop + within the next-hop-group."; + uses aft-nhg-nh-config; + uses aft-nhg-nh-state; + } + } + } + } + } + } + + grouping aft-nhg-config { + description + "Configuration parameters related to a next-hop-group."; + + leaf id { + type uint64; + description + "A unique identifier for the next-hop-group. This index + is not expected to be consistent across reboots, or + reprogramming of the next-hop-group. When updating + a next-hop-group, if the group is removed by the system + or assigned an alternate identifier, the system should + send telemetry notifications deleting the previous + identifier. If the identifier of the next-hop-group + is changed, all AFT entries that reference it must + also be updated."; + } + } + + grouping aft-nhg-state { + description + "Operational state parameters related to a next-hop-group."; + + leaf color { + type uint64; + description + + "An arbitrary colour that is used as an identifier for the next-hop + group. Some next-hop resolutions may utilise the colour to select + the particular next-hop-group that a routing entry should be resolved + to. In this case, next-hop-group selection may be based on colour + matches rather than the protocol specified next-hop. + + Regardless of whether the next-hop-group's specified colour is + used to select an AFT's active forwarding entry, the next-hop-group + referenced by an entry should be the currently active value. + + Next-hop-groups that are installed on the system through a protocol + that allows injection of such entries (e.g., BGP using the SR-TE + Policy SAFI, or gRPC-based RIB programming) should have the colour + specified in the injecting protocol within this leaf."; + } + + leaf backup-next-hop-group { + // We are at afts/next-hop-groups/next-hop-group/config/backup-next-hop-group + type leafref { + path "../../../next-hop-group/state/id"; + } + description + "The backup next-hop-group for the current group. When all + entries within the next-hop group become unusable, the backup + next-hop group is used if specified."; + } + } + + grouping aft-nhg-nh-config { + description + "Configuration parameters relating to an individual next-hop within + a next-hop-group."; + + leaf index { + type leafref { + // We are at afts/next-hop-groups/next-hop-group/next-hops/next-hop/config/id + path "../../../../../../next-hops/next-hop/config/index"; + } + description + "A reference to the identifier for the next-hop to which + the entry in the next-hop group corresponds."; + } + } + + grouping aft-nhg-nh-state { + description + "Operational state parameters relating to an individual next-hop + within the next-hop-group."; + + leaf weight { + type uint64; + description + "The weight applied to the next-hop within the group. Traffic + is balanced across the next-hops within the group in the + proportion of weight/(sum of weights of the next-hops within + the next-hop group)."; + } + } +} diff --git a/models/yang/common/openconfig-aft-ethernet.yang b/models/yang/common/openconfig-aft-ethernet.yang new file mode 100644 index 0000000000..d3bc40a59c --- /dev/null +++ b/models/yang/common/openconfig-aft-ethernet.yang @@ -0,0 +1,99 @@ +submodule openconfig-aft-ethernet { + belongs-to "openconfig-aft" { + prefix "oc-aft"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-yang-types { prefix "oc-yang"; } + + // Include common cross-AFT groupings from the common submodule. + include openconfig-aft-common; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Submodule containing definitions of groupings for the abstract + forwarding tables for Ethernet."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + grouping aft-ethernet-structural { + description + "Structural grouping defining the schema for the Ethernet + abstract forwarding table."; + + list mac-entry { + key "mac-address"; + + description + "List of the Ethernet entries within the abstract + forwarding table. This list is keyed by the outer MAC address + of the Ethernet frame."; + + leaf mac-address { + type leafref { + path "../config/mac-address"; + } + description + "Reference to the outer MAC address matched by the + entry."; + } + + container config { + description + "Configuration parameters for the Ethernet AFT entry."; + uses aft-ethernet-entry-config; + } + + container state { + config false; + description + "Operational state parameters for the Ethernet AFT + entry."; + uses aft-ethernet-entry-config; + uses aft-ethernet-entry-state; + } + } + } + + grouping aft-ethernet-entry-config { + description + "Configuration parameters for the Ethernet AFT entry."; + + leaf mac-address { + type oc-yang:mac-address; + description + "The outer MAC address of the Ethernet frame that must + be matched for the AFT entry to be utilised."; + } + } + + grouping aft-ethernet-entry-state { + description + "Operational state parameters for the Ethernet AFT entry."; + uses aft-common-entry-state; + } +} diff --git a/models/yang/common/openconfig-aft-ipv4.yang b/models/yang/common/openconfig-aft-ipv4.yang new file mode 100644 index 0000000000..8379464ecd --- /dev/null +++ b/models/yang/common/openconfig-aft-ipv4.yang @@ -0,0 +1,100 @@ +submodule openconfig-aft-ipv4 { + belongs-to "openconfig-aft" { + prefix "oc-aft"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-inet-types { prefix "oc-inet"; } + + // Include common cross-AFT groupings from the common submodule. + include openconfig-aft-common; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Submodule containing definitions of groupings for the abstract + forwarding tables for IPv4."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + grouping aft-ipv4-unicast-structural { + description + "Structural grouping defining the schema for the IPv4 unicast + abstract forwarding table."; + + list ipv4-entry { + key "prefix"; + + description + "List of the IPv4 unicast entries within the abstract + forwarding table. This list is keyed by the destination IPv4 + prefix."; + + leaf prefix { + type leafref { + path "../config/prefix"; + } + description + "Reference to the IPv4 unicast destination prefix which + must be matched to utilise the AFT entry."; + } + + container config { + description + "Configuration parameters for the IPv4 unicast AFT entry."; + uses aft-ipv4-unicast-entry-config; + } + + container state { + config false; + description + "Operational state parameters for the IPv4 unicast AFT + entry."; + uses aft-ipv4-unicast-entry-config; + uses aft-ipv4-unicast-entry-state; + } + } + } + + grouping aft-ipv4-unicast-entry-config { + description + "Configuration parameters for the IPv4 unicast entry."; + + leaf prefix { + type oc-inet:ipv4-prefix; + description + "The IPv4 destination prefix that should be matched to + utilise the AFT entry."; + } + } + + grouping aft-ipv4-unicast-entry-state { + description + "Operational state parameters for the IPv4 unicast entry."; + uses aft-common-entry-state; + uses aft-common-ip-state; + } +} diff --git a/models/yang/common/openconfig-aft-ipv6.yang b/models/yang/common/openconfig-aft-ipv6.yang new file mode 100644 index 0000000000..b14e78b81e --- /dev/null +++ b/models/yang/common/openconfig-aft-ipv6.yang @@ -0,0 +1,100 @@ +submodule openconfig-aft-ipv6 { + belongs-to "openconfig-aft" { + prefix "oc-aft"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-inet-types { prefix "oc-inet"; } + + // Include common cross-AFT groupings from the common submodule. + include openconfig-aft-common; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Submodule containing definitions of groupings for the abstract + forwarding tables for IPv6."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + grouping aft-ipv6-unicast-structural { + description + "Structural grouping defining the schema for the IPv6 unicast + abstract forwarding table."; + + list ipv6-entry { + key "prefix"; + + description + "List of the IPv6 unicast entries within the abstract + forwarding table. This list is keyed by the destination IPv6 + prefix."; + + leaf prefix { + type leafref { + path "../config/prefix"; + } + description + "Reference to the IPv6 unicast destination prefix which + must be matched to utilise the AFT entry."; + } + + container config { + description + "Configuration parameters for the IPv6 unicast AFT entry."; + uses aft-ipv6-unicast-entry-config; + } + + container state { + config false; + description + "Operational state parameters for the IPv6 unicast AFT + entry."; + uses aft-ipv6-unicast-entry-config; + uses aft-ipv6-unicast-entry-state; + } + } + } + + grouping aft-ipv6-unicast-entry-config { + description + "Configuration parameters for the IPv6 unicast entry."; + + leaf prefix { + type oc-inet:ipv6-prefix; + description + "The IPv6 destination prefix that should be matched to + utilise the AFT entry."; + } + } + + grouping aft-ipv6-unicast-entry-state { + description + "Operational state parameters for the IPv6 unicast entry."; + uses aft-common-entry-state; + uses aft-common-ip-state; + } +} diff --git a/models/yang/common/openconfig-aft-mpls.yang b/models/yang/common/openconfig-aft-mpls.yang new file mode 100644 index 0000000000..67e42bf7de --- /dev/null +++ b/models/yang/common/openconfig-aft-mpls.yang @@ -0,0 +1,117 @@ +submodule openconfig-aft-mpls { + belongs-to "openconfig-aft" { + prefix "oc-aft"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-mpls-types { prefix "oc-mplst"; } + + // Include common cross-AFT groupings from the common submodule. + include openconfig-aft-common; + + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Submodule containing definitions of groupings for the abstract + forwarding table for MPLS label forwarding."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + grouping aft-mpls-structural { + description + "Structural grouping defining the schema for the MPLS + abstract forwarding table."; + + list label-entry { + key "label"; + + description + "List of the MPLS entries within the abstract + forwarding table. This list is keyed by the top-most MPLS + label."; + + leaf label { + type leafref { + path "../config/label"; + } + description + "Reference to the top-most MPLS label matched by the + entry."; + } + + container config { + description + "Configuration parameters for the MPLS AFT entry."; + uses aft-mpls-entry-config; + } + + container state { + config false; + description + "Operational state parameters for the MPLS AFT + entry."; + uses aft-mpls-entry-config; + uses aft-mpls-entry-state; + } + } + } + + grouping aft-mpls-entry-config { + description + "Configuration parameters for the MPLS entry."; + + leaf label { + type oc-mplst:mpls-label; + description + "The top-most MPLS label that should be matched to + utilise the AFT entry."; + } + } + + grouping aft-mpls-entry-state { + description + "Operational state parameters for the MPLS entry."; + uses aft-common-entry-state; + + leaf-list popped-mpls-label-stack { + type oc-mplst:mpls-label; + description + "The MPLS label stack to be popped from the packet when + switched by the system. The stack is encoded as a leaf-list + such that the first entry is the label that is outer-most (i.e., + furthest from the bottom of the stack). + + If the local system pops the outer-most label 400, then the + value of this list is [400,]. If the local system removes two + labels, the outer-most being 500, and the second of which is + 400, then the value of the list is [500, 400]. + + A swap operation is reflected by entries in the + popped-mpls-label-stack and pushed-mpls-label-stack nodes."; + } + } +} diff --git a/models/yang/common/openconfig-aft-pf.yang b/models/yang/common/openconfig-aft-pf.yang new file mode 100644 index 0000000000..e8b771e96d --- /dev/null +++ b/models/yang/common/openconfig-aft-pf.yang @@ -0,0 +1,178 @@ +submodule openconfig-aft-pf { + belongs-to "openconfig-aft" { + prefix "oc-aft"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-inet-types { prefix "oc-inet"; } + import openconfig-yang-types { prefix "oc-yang"; } + import openconfig-mpls-types { prefix "oc-mplst"; } + import openconfig-packet-match-types { + prefix "oc-pkt-match-types"; + } + + // Include common cross-AFT groupings from the common submodule. + include openconfig-aft-common; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Submodule containing definitions of groupings for the abstract + forwarding table(s) for policy forwarding entries. These are + defined to be forwarding tables that allow matches on + fields other than the destination address that is used in + other forwarding tables."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + grouping aft-pf-structural { + description + "Structural grouping defining the schema for the policy + forwarding abstract forwarding table."; + + list policy-forwarding-entry { + key "index"; + + description + "List of the policy forwarding entries within the abstract + forwarding table. Each entry is uniquely identified by an + index on the system, due to the arbitrary match conditions + that may be implemented within the policy forwarding AFT. + The index may change upon changes of the entry if, and only + if, the device exporting the AFT replaces the entire entry + by removing the previous entry and replacing it with a + subsequent updated version."; + + leaf index { + type leafref { + path "../config/index"; + } + description + "Reference to the arbitary index for the policy forwarding + AFT entry."; + } + + container config { + description + "Configuration parameters for the Policy forwarding + AFT entry."; + uses aft-pf-entry-config; + } + + container state { + config false; + description + "Operational state parameters for the Policy Forwarding + AFT entry."; + uses aft-pf-entry-config; + uses aft-pf-entry-state; + } + } + } + + grouping aft-pf-entry-config { + description + "Configuration parameters for the Policy Forwarding + AFT entry."; + + leaf index { + type uint64; + description + "An arbitrary 64-bit index identifying the policy forwarding + AFT entry."; + } + + leaf ip-prefix { + type oc-inet:ip-prefix; + description + "The IP prefix that the forwarding entry matches."; + } + + leaf mac-address { + type oc-yang:mac-address; + description + "The MAC address that the forwarding entry matches. Used for + Layer 2 forwarding entries, e.g., within a VSI instance."; + } + + leaf mpls-label { + type oc-mplst:mpls-label; + description + "The MPLS label that the forwarding entry matches. Used for + MPLS forwarding entries, whereby the local device acts as an + LSR."; + } + + leaf mpls-tc { + type oc-mplst:mpls-tc; + description + "The value of the MPLS Traffic Class bits (formerly known as + the MPLS experimental bits) that are to be matched by the AFT + entry."; + reference + "RFC5462: Multiprotocol Label Switching (MPLS) Label Stack + Entry: 'EXP' Field Renamed to 'Traffic Class' Field"; } + + leaf ip-dscp { + type oc-inet:dscp; + description + "The value of the differentiated services code point (DSCP) to + be matched for the forwarding entry. The value is specified in + cases where specific class-based forwarding based on IP is + implemented by the device."; + } + + leaf ip-protocol { + type oc-pkt-match-types:ip-protocol-type; + description + "The value of the IP protocol field of an IPv4 packet, or the + next-header field of an IPv6 packet which is to be matched by + the AFT entry. This field is utilised where forwarding is + performed based on L4 information."; + } + + leaf l4-src-port { + type oc-inet:port-number; + description + "The value of the source port field of the transport header + that is to be matched by the AFT entry."; + } + + leaf l4-dst-port { + type oc-inet:port-number; + description + "The value of the destination port field of the transport + header that is to be matched by the AFT entry."; + } + } + + grouping aft-pf-entry-state { + description + "Operational state parameters for the Policy Forwarding + AFT entry."; + uses aft-common-entry-state; + } +} diff --git a/models/yang/common/openconfig-aft-types.yang b/models/yang/common/openconfig-aft-types.yang new file mode 100644 index 0000000000..6ae2a32eb5 --- /dev/null +++ b/models/yang/common/openconfig-aft-types.yang @@ -0,0 +1,69 @@ +module openconfig-aft-types { + + namespace "http://openconfig.net/yang/fib-types"; + prefix "oc-aftt"; + + import openconfig-extensions { prefix "oc-ext"; } + + organization + "OpenConfig Working Group"; + + contact + "OpenConfig Working Group + www.openconfig.net"; + + description + "Types related to the OpenConfig Abstract Forwarding + Table (AFT) model"; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + typedef encapsulation-header-type { + type enumeration { + enum GRE { + description + "The encapsulation header is a Generic Routing Encapsulation + header."; + } + enum IPV4 { + description + "The encapsulation header is an IPv4 packet header"; + } + enum IPV6 { + description + "The encapsulation header is an IPv6 packet header"; + } + enum MPLS { + description + "The encapsulation header is one or more MPLS labels indicated + by the pushed and popped label stack lists."; + } + } + description + "Types of tunnel encapsulation that are supported by systems as either + head- or tail-end."; + } +} diff --git a/models/yang/common/openconfig-aft.yang b/models/yang/common/openconfig-aft.yang new file mode 100644 index 0000000000..3cdfdb8ba7 --- /dev/null +++ b/models/yang/common/openconfig-aft.yang @@ -0,0 +1,155 @@ +module openconfig-aft { + + yang-version "1"; + + namespace "http://openconfig.net/yang/aft"; + + prefix "oc-aft"; + + import openconfig-extensions { prefix "oc-ext"; } + + // Include IPv4 AFT submodule. + include openconfig-aft-ipv4; + // Include IPv6 AFT submodule. + include openconfig-aft-ipv6; + // Include MPLS AFT submodule. + include openconfig-aft-mpls; + // Include policy forwarding AFT submodule. + include openconfig-aft-pf; + // Include the ethernet AFT submodule. + include openconfig-aft-ethernet; + // Include the common cross-AFT entities. + include openconfig-aft-common; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "A model describing the forwarding entries installed on a network + element. It should be noted that this model is not expected to + align 1:1 with the underlying structure used directly by a + forwarding element (e.g., linecard), but rather provide an + abstraction that can be consumed by an NMS to observe, and in some + cases manipulate, the internal forwarding database in a simplified + manner. Since the underlying model of the forwarding table is not + expected to align with this model, the structure described herein + is referred to as an Abstract Forwarding Table (AFT), rather than + the FIB."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Formatting fixes"; + reference "0.3.1"; + } + + revision 2017-05-10 { + description + "Refactor to provide concretised per-AF schemas per AFT."; + reference "0.3.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // config + state groupings + + // structural groupings + + grouping aft-top { + description + "Top-level grouping allowing per-protocol instantiation of the + AFT."; + + container afts { + description + "The abstract forwarding tables (AFTs) that are associated + with the network instance. An AFT is instantiated per-protocol + running within the network-instance - such that one exists for + IPv4 Unicast, IPv6 Unicast, MPLS, L2 forwarding entries, etc. + A forwarding entry within the FIB has a set of next-hops, + which may be a reference to an entry within another table - + e.g., where a Layer 3 next-hop has an associated Layer 2 + forwarding entry."; + + container ipv4-unicast { + description + "The abstract forwarding table for IPv4 unicast. Entries + within this table are uniquely keyed on the IPv4 unicast + destination prefix which is matched by ingress packets. + + The data set represented by the IPv4 Unicast AFT is the set + of entries from the IPv4 unicast RIB that have been selected + for installation into the FIB of the device exporting the + data structure."; + + uses aft-ipv4-unicast-structural; + } + + container ipv6-unicast { + description + "The abstract forwarding table for IPv6 unicast. Entries + within this table are uniquely keyed on the IPv6 unicast + destination prefix which is matched by ingress packets. + + The data set represented by the IPv6 Unicast AFTis the set + of entries within the IPv6 RIB that "; + + uses aft-ipv6-unicast-structural; + + } + + container policy-forwarding { + description + "The abstract forwarding table for policy-based forwarding + entries. Since multiple match criteria can be utilised + within a policy-based forwarding rule, this AFT provides a + flexible match criteria, and is indexed based on an + arbitrary 64-bit index. Entries within the AFT may match on + multiple field types (e.g., L4 header fields, as well as L2 + fields). + + Examples of entries within this table are: + - IPv4 policy-based routing based on DSCP. + - MPLS policy-based forwarding entries."; + + uses aft-pf-structural; + } + + container mpls { + description + "The abstract forwarding table for MPLS label based + forwarding entries. Entries within the table are keyed based + on the top-most MPLS label in the stack on the ingress + packet."; + + uses aft-mpls-structural; + } + + container ethernet { + description + "The abstract forwarding table for Ethernet based forwarding + entries. Entries within the table are keyed based on the + destination MAC address on the ingress packet."; + + uses aft-ethernet-structural; + } + + uses aft-next-hop-groups-structural; + uses aft-nhop-structural; + } + } +} diff --git a/models/yang/common/openconfig-alarm-types.yang b/models/yang/common/openconfig-alarm-types.yang new file mode 100644 index 0000000000..c4617b5e6b --- /dev/null +++ b/models/yang/common/openconfig-alarm-types.yang @@ -0,0 +1,150 @@ +module openconfig-alarm-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/alarms/types"; + + prefix "oc-alarm-types"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines operational state data related to alarms + that the device is reporting. + + This model reuses some data items defined in the draft IETF + YANG Alarm Module: + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02 + + Portions of this code were derived from the draft IETF YANG Alarm + Module. Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2018-01-16" { + description + "Moved alarm identities into separate types module"; + reference "0.2.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + identity OPENCONFIG_ALARM_TYPE_ID { + description + "Base identity for alarm type ID profiles"; + } + + identity AIS { + base OPENCONFIG_ALARM_TYPE_ID; + description + "Defines an alarm indication signal type of alarm"; + } + + identity EQPT { + base OPENCONFIG_ALARM_TYPE_ID; + description + "Defines an equipment related type of alarm that is specific + to the physical hardware"; + } + + identity LOS { + base OPENCONFIG_ALARM_TYPE_ID; + description + "Defines a loss of signal type of alarm"; + } + + identity OTS { + base OPENCONFIG_ALARM_TYPE_ID; + description + "Defines a optical transport signal type of alarm"; + } + + identity OPENCONFIG_ALARM_SEVERITY { + description + "Base identity for alarm severity profiles. Derived + identities are based on contents of the draft + IETF YANG Alarm Module"; + reference + "IETF YANG Alarm Module: Draft - typedef severity + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02"; + + } + + identity UNKNOWN { + base OPENCONFIG_ALARM_SEVERITY; + description + "Indicates that the severity level could not be determined. + This level SHOULD be avoided."; + } + + identity MINOR { + base OPENCONFIG_ALARM_SEVERITY; + description + "Indicates the existence of a non-service affecting fault + condition and that corrective action should be taken in + order to prevent a more serious (for example, service + affecting) fault. Such a severity can be reported, for + example, when the detected alarm condition is not currently + degrading the capacity of the resource"; + } + + identity WARNING { + base OPENCONFIG_ALARM_SEVERITY; + description + "Indicates the detection of a potential or impending service + affecting fault, before any significant effects have been felt. + Action should be taken to further diagnose (if necessary) and + correct the problem in order to prevent it from becoming a more + serious service affecting fault."; + } + + identity MAJOR { + base OPENCONFIG_ALARM_SEVERITY; + description + "Indicates that a service affecting condition has developed + and an urgent corrective action is required. Such a severity + can be reported, for example, when there is a severe + degradation in the capability of the resource and its full + capability must be restored."; + } + + identity CRITICAL { + base OPENCONFIG_ALARM_SEVERITY; + description + "Indicates that a service affecting condition has occurred + and an immediate corrective action is required. Such a + severity can be reported, for example, when a resource becomes + totally out of service and its capability must be restored."; + } + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-alarms.yang b/models/yang/common/openconfig-alarms.yang new file mode 100644 index 0000000000..5bcc563149 --- /dev/null +++ b/models/yang/common/openconfig-alarms.yang @@ -0,0 +1,231 @@ +module openconfig-alarms { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/alarms"; + + prefix "oc-alarms"; + + // import some basic types + import openconfig-alarm-types { prefix oc-alarm-types; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-types { prefix oc-types; } + import openconfig-platform { prefix oc-platform; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines operational state data related to alarms + that the device is reporting. + + This model reuses some data items defined in the draft IETF + YANG Alarm Module: + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02 + + Portions of this code were derived from the draft IETF YANG Alarm + Module. Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "0.3.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2018-01-16" { + description + "Moved alarm identities into separate types module"; + reference "0.3.0"; + } + + revision "2018-01-10" { + description + "Make alarms list read only"; + reference "0.2.0"; + } + + revision "2017-08-24" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + grouping alarm-state { + description + "Operational state data for device alarms"; + + leaf id { + type string; + description + "Unique ID for the alarm -- this will not be a + configurable parameter on many implementations"; + } + + leaf resource { + type string; + description + "The item that is under alarm within the device. The + resource may be a reference to an item which is + defined elsewhere in the model. For example, it + may be a platform/component, interfaces/interface, + terminal-device/logical-channels/channel, etc. In this + case the system should match the name of the referenced + item exactly. The referenced item could alternatively be + the path of the item within the model."; + reference + "IETF YANG Alarm Module: Draft - typedef resource + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02"; + } + + leaf text { + type string; + description + "The string used to inform operators about the alarm. This + MUST contain enough information for an operator to be able + to understand the problem. If this string contains structure, + this format should be clearly documented for programs to be + able to parse that information"; + reference + "IETF YANG Alarm Module: Draft - typedef alarm-text + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02"; + } + + leaf time-created { + type oc-types:timeticks64; + description + "The time at which the alarm was raised by the system. + This value is expressed as nanoseconds since the Unix Epoch"; + } + + leaf severity { + type identityref { + base oc-alarm-types:OPENCONFIG_ALARM_SEVERITY; + } + description + "The severity level indicating the criticality and impact + of the alarm"; + reference + "IETF YANG Alarm Module: Draft - typedef severity + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02"; + } + + leaf type-id { + type union { + type string; + type identityref { + base oc-alarm-types:OPENCONFIG_ALARM_TYPE_ID; + } + } + description + "The abbreviated name of the alarm, for example LOS, + EQPT, or OTS. Also referred to in different systems as + condition type, alarm identifier, or alarm mnemonic. It + is recommended to use the OPENCONFIG_ALARM_TYPE_ID + identities where possible and only use the string type + when the desired identityref is not yet defined"; + reference + "IETF YANG Alarm Module: Draft - typedef alarm-type-id + https://tools.ietf.org/html/draft-vallin-netmod-alarm-module-02"; + } + } + + grouping alarm-config { + description + "Configuration data for device alarms"; + } + + grouping alarms-top { + description + "Top-level grouping for device alarms"; + + container alarms { + description + "Top-level container for device alarms"; + + config false; + + list alarm { + key "id"; + description + "List of alarms, keyed by a unique id"; + + leaf id { + type leafref { + path "../state/id"; + } + + description + "References the unique alarm id"; + } + + container config { + description + "Configuration data for each alarm"; + + uses alarm-config; + } + + container state { + config false; + + description + "Operational state data for a device alarm"; + + uses alarm-config; + uses alarm-state; + } + } + } + } + + + // augments + + augment "/oc-platform:components/oc-platform:component/oc-platform:state" { + description + "Adds specific alarms related to a component."; + + leaf equipment-failure { + type boolean; + default "false"; + description + "If true, the hardware indicates that the component's physical equipment + has failed"; + } + + leaf equipment-mismatch { + type boolean; + default "false"; + description + "If true, the hardware indicates that the component inserted into the + affected component's physical location is of a different type than what + is configured"; + } + } + +} diff --git a/models/yang/common/openconfig-bgp-common-multiprotocol.yang b/models/yang/common/openconfig-bgp-common-multiprotocol.yang new file mode 100644 index 0000000000..a39eff345f --- /dev/null +++ b/models/yang/common/openconfig-bgp-common-multiprotocol.yang @@ -0,0 +1,530 @@ +submodule openconfig-bgp-common-multiprotocol { + + belongs-to openconfig-bgp { + prefix "oc-bgp"; + } + + import openconfig-extensions { prefix oc-ext; } + import openconfig-bgp-types { prefix oc-bgp-types; } + import openconfig-routing-policy { prefix oc-rpol; } + import openconfig-types { prefix oc-types; } + + include openconfig-bgp-common; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This sub-module contains groupings that are related to support + for multiple protocols in BGP. The groupings are common across + multiple contexts."; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + grouping bgp-common-mp-afi-safi-graceful-restart-config { + description + "BGP graceful restart parameters that apply on a per-AFI-SAFI + basis"; + + leaf enabled { + type boolean; + default false; + description + "This leaf indicates whether graceful-restart is enabled for + this AFI-SAFI"; + } + } + + grouping bgp-common-mp-afi-safi-config { + description + "Configuration parameters used for all BGP AFI-SAFIs"; + + leaf afi-safi-name { + type identityref { + base oc-bgp-types:AFI_SAFI_TYPE; + } + description "AFI,SAFI"; + } + + leaf enabled { + type boolean; + default false; + description + "This leaf indicates whether the AFI-SAFI is + enabled for the neighbour or group"; + } + } + + grouping bgp-common-mp-all-afi-safi-list-contents { + description + "A common grouping used for contents of the list that is used + for AFI-SAFI entries"; + + // import and export policy included for the afi/safi + uses oc-rpol:apply-policy-group; + + uses bgp-common-mp-ipv4-unicast-group; + uses bgp-common-mp-ipv6-unicast-group; + uses bgp-common-mp-ipv4-labeled-unicast-group; + uses bgp-common-mp-ipv6-labeled-unicast-group; + uses bgp-common-mp-l3vpn-ipv4-unicast-group; + uses bgp-common-mp-l3vpn-ipv6-unicast-group; + uses bgp-common-mp-l3vpn-ipv4-multicast-group; + uses bgp-common-mp-l3vpn-ipv6-multicast-group; + uses bgp-common-mp-l2vpn-vpls-group; + uses bgp-common-mp-l2vpn-evpn-group; + uses bgp-common-mp-srte-policy-ipv4-group; + uses bgp-common-mp-srte-policy-ipv6-group; + } + + // Groupings relating to each address family + grouping bgp-common-mp-ipv4-unicast-group { + description + "Group for IPv4 Unicast configuration options"; + + container ipv4-unicast { + when "../afi-safi-name = 'oc-bgp-types:IPV4_UNICAST'" { + description + "Include this container for IPv4 Unicast specific + configuration"; + } + + description "IPv4 unicast configuration options"; + + // include common IPv[46] unicast options + uses bgp-common-mp-ipv4-ipv6-unicast-common; + + // placeholder for IPv4 unicast specific configuration + } + } + + grouping bgp-common-mp-ipv6-unicast-group { + description + "Group for IPv6 Unicast configuration options"; + + container ipv6-unicast { + when "../afi-safi-name = 'oc-bgp-types:IPV6_UNICAST'" { + description + "Include this container for IPv6 Unicast specific + configuration"; + } + + description "IPv6 unicast configuration options"; + + // include common IPv[46] unicast options + uses bgp-common-mp-ipv4-ipv6-unicast-common; + + // placeholder for IPv6 unicast specific configuration + // options + } + } + + grouping bgp-common-mp-ipv4-labeled-unicast-group { + description + "Group for IPv4 Labeled Unicast configuration options"; + + container ipv4-labeled-unicast { + when "../afi-safi-name = 'oc-bgp-types:IPV4_LABELED_UNICAST'" { + description + "Include this container for IPv4 Labeled Unicast specific + configuration"; + } + + description "IPv4 Labeled Unicast configuration options"; + + uses bgp-common-mp-all-afi-safi-common; + + // placeholder for IPv4 Labeled Unicast specific config + // options + } + } + + grouping bgp-common-mp-ipv6-labeled-unicast-group { + description + "Group for IPv6 Labeled Unicast configuration options"; + + container ipv6-labeled-unicast { + when "../afi-safi-name = 'oc-bgp-types:IPV6_LABELED_UNICAST'" { + description + "Include this container for IPv6 Labeled Unicast specific + configuration"; + } + + description "IPv6 Labeled Unicast configuration options"; + + uses bgp-common-mp-all-afi-safi-common; + + // placeholder for IPv6 Labeled Unicast specific config + // options. + } + } + + grouping bgp-common-mp-l3vpn-ipv4-unicast-group { + description + "Group for IPv4 Unicast L3VPN configuration options"; + + container l3vpn-ipv4-unicast { + when "../afi-safi-name = 'oc-bgp-types:L3VPN_IPV4_UNICAST'" { + description + "Include this container for IPv4 Unicast L3VPN specific + configuration"; + } + + description "Unicast IPv4 L3VPN configuration options"; + + // include common L3VPN configuration options + uses bgp-common-mp-l3vpn-ipv4-ipv6-unicast-common; + + // placeholder for IPv4 Unicast L3VPN specific config options. + } + } + + grouping bgp-common-mp-l3vpn-ipv6-unicast-group { + description + "Group for IPv6 Unicast L3VPN configuration options"; + + container l3vpn-ipv6-unicast { + when "../afi-safi-name = 'oc-bgp-types:L3VPN_IPV6_UNICAST'" { + description + "Include this container for unicast IPv6 L3VPN specific + configuration"; + } + + description "Unicast IPv6 L3VPN configuration options"; + + // include common L3VPN configuration options + uses bgp-common-mp-l3vpn-ipv4-ipv6-unicast-common; + + // placeholder for IPv6 Unicast L3VPN specific configuration + // options + } + } + + grouping bgp-common-mp-l3vpn-ipv4-multicast-group { + description + "Group for IPv4 L3VPN multicast configuration options"; + + container l3vpn-ipv4-multicast { + when "../afi-safi-name = 'oc-bgp-types:L3VPN_IPV4_MULTICAST'" { + description + "Include this container for multicast IPv6 L3VPN specific + configuration"; + } + + description "Multicast IPv4 L3VPN configuration options"; + + // include common L3VPN multicast options + uses bgp-common-mp-l3vpn-ipv4-ipv6-multicast-common; + + // placeholder for IPv4 Multicast L3VPN specific configuration + // options + } + } + + grouping bgp-common-mp-l3vpn-ipv6-multicast-group { + description + "Group for IPv6 L3VPN multicast configuration options"; + + container l3vpn-ipv6-multicast { + when "../afi-safi-name = 'oc-bgp-types:L3VPN_IPV6_MULTICAST'" { + description + "Include this container for multicast IPv6 L3VPN specific + configuration"; + } + + description "Multicast IPv6 L3VPN configuration options"; + + // include common L3VPN multicast options + uses bgp-common-mp-l3vpn-ipv4-ipv6-multicast-common; + + // placeholder for IPv6 Multicast L3VPN specific configuration + // options + } + } + + grouping bgp-common-mp-l2vpn-vpls-group { + description + "Group for BGP-signalled VPLS configuration options"; + + container l2vpn-vpls { + when "../afi-safi-name = 'oc-bgp-types:L2VPN_VPLS'" { + description + "Include this container for BGP-signalled VPLS specific + configuration"; + } + + description "BGP-signalled VPLS configuration options"; + + // include common L2VPN options + uses bgp-common-mp-l2vpn-common; + + // placeholder for BGP-signalled VPLS specific configuration + // options + } + } + + grouping bgp-common-mp-l2vpn-evpn-group { + description + "Group for BGP EVPN configuration options"; + + container l2vpn-evpn { + when "../afi-safi-name = 'oc-bgp-types:L2VPN_EVPN'" { + description + "Include this container for BGP EVPN specific + configuration"; + } + + description "BGP EVPN configuration options"; + + // include common L2VPN options + uses bgp-common-mp-l2vpn-common; + + // placeholder for BGP EVPN specific configuration options + } + } + + // Common groupings across multiple AFI,SAFIs + grouping bgp-common-mp-all-afi-safi-common { + description + "Grouping for configuration common to all AFI,SAFI"; + + container prefix-limit { + description + "Configure the maximum number of prefixes that will be + accepted from a peer"; + + container config { + description + "Configuration parameters relating to the prefix + limit for the AFI-SAFI"; + uses bgp-common-mp-all-afi-safi-common-prefix-limit-config; + } + + container state { + config false; + description + "State information relating to the prefix-limit for the + AFI-SAFI"; + uses bgp-common-mp-all-afi-safi-common-prefix-limit-config; + } + } + } + + grouping bgp-common-mp-ipv4-ipv6-unicast-common { + description + "Common configuration that is applicable for IPv4 and IPv6 + unicast"; + + // include common afi-safi options. + uses bgp-common-mp-all-afi-safi-common; + + // configuration options that are specific to IPv[46] unicast + container config { + description + "Configuration parameters for common IPv4 and IPv6 unicast + AFI-SAFI options"; + uses bgp-common-mp-ipv4-ipv6-unicast-common-config; + } + container state { + config false; + description + "State information for common IPv4 and IPv6 unicast + parameters"; + uses bgp-common-mp-ipv4-ipv6-unicast-common-config; + } + } + + grouping bgp-common-mp-l3vpn-ipv4-ipv6-unicast-common { + description + "Common configuration applied across L3VPN for IPv4 + and IPv6"; + + // placeholder -- specific configuration options that are generic + // across IPv[46] unicast address families. + uses bgp-common-mp-all-afi-safi-common; + } + + grouping bgp-common-mp-l3vpn-ipv4-ipv6-multicast-common { + description + "Common configuration applied across L3VPN for IPv4 + and IPv6"; + + // placeholder -- specific configuration options that are + // generic across IPv[46] multicast address families. + uses bgp-common-mp-all-afi-safi-common; + } + + grouping bgp-common-mp-l2vpn-common { + description + "Common configuration applied across L2VPN address + families"; + + // placeholder -- specific configuration options that are + // generic across L2VPN address families + uses bgp-common-mp-all-afi-safi-common; + } + + grouping bgp-common-mp-srte-policy-ipv4-group { + description + "Grouping for SR-TE for AFI 1"; + + container srte-policy-ipv4 { + when "../afi-safi-name = 'oc-bgp-types:SRTE_POLICY_IPV4'" { + description + "Only include this container when the address family is + specified to be SR-TE Policy SAFI for the IPv4 unicast + address family."; + } + + description + "Configuration and operational state parameters relating to + the SR-TE Policy SAFI for IPv4 Unicast."; + + uses bgp-common-mp-all-afi-safi-common; + } + } + + grouping bgp-common-mp-srte-policy-ipv6-group { + description + "Grouping for SR-TE for AFI 2"; + + container srte-policy-ipv6 { + when "../afi-safi-name = 'oc-bgp-types:SRTE_POLICY_IPV6'" { + description + "Only include this container when the address family is + specified to be SR-TE Policy SAFI for the IPv6 unicast + address family."; + } + + description + "Configuration and operational state parameters relating to + the SR-TE Policy SAFI for IPv6 Unicast."; + + uses bgp-common-mp-all-afi-safi-common; + } + } + + // Config groupings for common groups + grouping bgp-common-mp-all-afi-safi-common-prefix-limit-config { + description + "Configuration parameters relating to prefix-limits for an + AFI-SAFI"; + + leaf max-prefixes { + type uint32; + description + "Maximum number of prefixes that will be accepted + from the neighbour"; + } + + leaf prevent-teardown { + type boolean; + default false; + description + "Do not tear down the BGP session when the maximum + prefix limit is exceeded, but rather only log a + warning. The default of this leaf is false, such + that when it is not specified, the session is torn + down."; + } + + leaf warning-threshold-pct { + type oc-types:percentage; + description + "Threshold on number of prefixes that can be received + from a neighbour before generation of warning messages + or log entries. Expressed as a percentage of + max-prefixes"; + } + + leaf restart-timer { + type decimal64 { + fraction-digits 2; + } + units "seconds"; + description + "Time interval in seconds after which the BGP session + is re-established after being torn down due to exceeding + the max-prefix limit."; + } + } + + grouping bgp-common-mp-ipv4-ipv6-unicast-common-config { + description + "Common configuration parameters for IPv4 and IPv6 Unicast + address families"; + + leaf send-default-route { + type boolean; + default "false"; + description + "If set to true, send the default-route to the neighbour(s)"; + } + } +} diff --git a/models/yang/common/openconfig-bgp-common-structure.yang b/models/yang/common/openconfig-bgp-common-structure.yang new file mode 100644 index 0000000000..7b29996971 --- /dev/null +++ b/models/yang/common/openconfig-bgp-common-structure.yang @@ -0,0 +1,208 @@ +submodule openconfig-bgp-common-structure { + + belongs-to openconfig-bgp { + prefix "oc-bgp"; + } + + import openconfig-extensions { prefix oc-ext; } + + include openconfig-bgp-common-multiprotocol; + include openconfig-bgp-common; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This sub-module contains groupings that are common across multiple BGP + contexts and provide structure around other primitive groupings."; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + grouping bgp-common-structure-neighbor-group-logging-options { + description + "Structural grouping used to include error handling configuration and + state for both BGP neighbors and groups"; + + container logging-options { + description + "Logging options for events related to the BGP neighbor or + group"; + container config { + description + "Configuration parameters enabling or modifying logging + for events relating to the BGPgroup"; + uses bgp-common-neighbor-group-logging-options-config; + } + container state { + config false; + description + "State information relating to logging for the BGP neighbor + or group"; + uses bgp-common-neighbor-group-logging-options-config; + } + } + } + + grouping bgp-common-structure-neighbor-group-ebgp-multihop { + description + "Structural grouping used to include eBGP multihop configuration and + state for both BGP neighbors and peer groups"; + + container ebgp-multihop { + description + "eBGP multi-hop parameters for the BGPgroup"; + container config { + description + "Configuration parameters relating to eBGP multihop for the + BGP group"; + uses bgp-common-neighbor-group-multihop-config; + } + container state { + config false; + description + "State information for eBGP multihop, for the BGP neighbor + or group"; + uses bgp-common-neighbor-group-multihop-config; + } + } + } + + grouping bgp-common-structure-neighbor-group-route-reflector { + description + "Structural grouping used to include route reflector configuration and + state for both BGP neighbors and peer groups"; + + container route-reflector { + description + "Route reflector parameters for the BGPgroup"; + container config { + description + "Configuraton parameters relating to route reflection + for the BGPgroup"; + uses bgp-common-neighbor-group-route-reflector-config; + } + container state { + config false; + description + "State information relating to route reflection for the + BGPgroup"; + uses bgp-common-neighbor-group-route-reflector-config; + } + } + } + + grouping bgp-common-structure-neighbor-group-as-path-options { + description + "Structural grouping used to include AS_PATH manipulation configuration + and state for both BGP neighbors and peer groups"; + + container as-path-options { + description + "AS_PATH manipulation parameters for the BGP neighbor or + group"; + container config { + description + "Configuration parameters relating to AS_PATH manipulation + for the BGP peer or group"; + uses bgp-common-neighbor-group-as-path-options-config; + } + container state { + config false; + description + "State information relating to the AS_PATH manipulation + mechanisms for the BGP peer or group"; + uses bgp-common-neighbor-group-as-path-options-config; + } + } + } + + grouping bgp-common-structure-neighbor-group-add-paths { + description + "Structural grouping used to include ADD-PATHs configuration and state + for both BGP neighbors and peer groups"; + + container add-paths { + description + "Parameters relating to the advertisement and receipt of + multiple paths for a single NLRI (add-paths)"; + container config { + description + "Configuration parameters relating to ADD_PATHS"; + uses bgp-common-neighbor-group-add-paths-config; + } + container state { + config false; + description + "State information associated with ADD_PATHS"; + uses bgp-common-neighbor-group-add-paths-config; + } + } + } + +} diff --git a/models/yang/common/openconfig-bgp-common.yang b/models/yang/common/openconfig-bgp-common.yang new file mode 100644 index 0000000000..94ba64ffe5 --- /dev/null +++ b/models/yang/common/openconfig-bgp-common.yang @@ -0,0 +1,677 @@ +submodule openconfig-bgp-common { + + belongs-to openconfig-bgp { + prefix "oc-bgp"; + } + + import openconfig-extensions { prefix oc-ext; } + import openconfig-bgp-types { prefix oc-bgp-types; } + import openconfig-routing-policy { prefix oc-rpol; } + import openconfig-types { prefix oc-types; } + import openconfig-inet-types { prefix oc-inet; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This sub-module contains common groupings that are common across + multiple contexts within the BGP module. That is to say that they + may be application to a subset of global, peer-group or neighbor + contexts."; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + grouping bgp-common-neighbor-group-timers-config { + description + "Config parameters related to timers associated with the BGP + peer"; + + leaf connect-retry { + type decimal64 { + fraction-digits 2; + } + default 30; + description + "Time interval in seconds between attempts to establish a + session with the peer."; + } + + leaf hold-time { + type decimal64 { + fraction-digits 2; + } + default 90; + description + "Time interval in seconds that a BGP session will be + considered active in the absence of keepalive or other + messages from the peer. The hold-time is typically + set to 3x the keepalive-interval."; + reference + "RFC 4271 - A Border Gateway Protocol 4, Sec. 10"; + } + + leaf keepalive-interval { + type decimal64 { + fraction-digits 2; + } + default 30; + description + "Time interval in seconds between transmission of keepalive + messages to the neighbor. Typically set to 1/3 the + hold-time."; + } + + leaf minimum-advertisement-interval { + type decimal64 { + fraction-digits 2; + } + default 30; + description + "Minimum time which must elapse between subsequent UPDATE + messages relating to a common set of NLRI being transmitted + to a peer. This timer is referred to as + MinRouteAdvertisementIntervalTimer by RFC 4721 and serves to + reduce the number of UPDATE messages transmitted when a + particular set of NLRI exhibit instability."; + reference + "RFC 4271 - A Border Gateway Protocol 4, Sec 9.2.1.1"; + } + } + + grouping bgp-common-neighbor-group-config { + description + "Neighbor level configuration items."; + + leaf peer-as { + type oc-inet:as-number; + description + "AS number of the peer."; + } + + leaf local-as { + type oc-inet:as-number; + description + "The local autonomous system number that is to be used + when establishing sessions with the remote peer or peer + group, if this differs from the global BGP router + autonomous system number."; + } + + leaf peer-type { + type oc-bgp-types:peer-type; + description + "Explicitly designate the peer or peer group as internal + (iBGP) or external (eBGP)."; + } + + leaf auth-password { + type oc-types:routing-password; + description + "Configures an MD5 authentication password for use with + neighboring devices."; + } + + leaf remove-private-as { + // could also make this a container with a flag to enable + // remove-private and separate option. here, option implies + // remove-private is enabled. + type oc-bgp-types:remove-private-as-option; + description + "Remove private AS numbers from updates sent to peers - when + this leaf is not specified, the AS_PATH attribute should be + sent to the peer unchanged"; + } + + leaf route-flap-damping { + type boolean; + default false; + description + "Enable route flap damping."; + } + + leaf send-community { + type oc-bgp-types:community-type; + default "NONE"; + description + "Specify which types of community should be sent to the + neighbor or group. The default is to not send the + community attribute"; + } + + leaf description { + type string; + description + "An optional textual description (intended primarily for use + with a peer or group"; + } + } + + grouping bgp-common-neighbor-group-transport-config { + description + "Configuration parameters relating to the transport protocol + used by the BGP session to the peer"; + + leaf tcp-mss { + type uint16; + description + "Sets the max segment size for BGP TCP sessions."; + } + + leaf mtu-discovery { + type boolean; + default false; + description + "Turns path mtu discovery for BGP TCP sessions on (true) + or off (false)"; + } + + leaf passive-mode { + type boolean; + default false; + description + "Wait for peers to issue requests to open a BGP session, + rather than initiating sessions from the local router."; + } + + leaf local-address { + type union { + type oc-inet:ip-address; + type string; + } + //TODO: the string should be converted to a leafref type + //to point to an interface when YANG 1.1 is available with + //leafrefs in union types. + description + "Set the local IP (either IPv4 or IPv6) address to use + for the session when sending BGP update messages. This + may be expressed as either an IP address or reference + to the name of an interface."; + } + } + + grouping bgp-common-neighbor-group-error-handling-config { + description + "Configuration parameters relating to enhanced error handling + behaviours for BGP"; + + leaf treat-as-withdraw { + type boolean; + default "false"; + description + "Specify whether erroneous UPDATE messages for which the + NLRI can be extracted are reated as though the NLRI is + withdrawn - avoiding session reset"; + reference "draft-ietf-idr-error-handling-16"; + } + } + + grouping bgp-common-neighbor-group-logging-options-config { + description + "Configuration parameters specifying the logging behaviour for + BGP sessions to the peer"; + + leaf log-neighbor-state-changes { + type boolean; + default "true"; + description + "Configure logging of peer state changes. Default is + to enable logging of peer state changes."; + } + } + + grouping bgp-common-neighbor-group-multihop-config { + description + "Configuration parameters specifying the multihop behaviour for + BGP sessions to the peer"; + + leaf enabled { + type boolean; + default "false"; + description + "When enabled the referenced group or neighbors are permitted + to be indirectly connected - including cases where the TTL + can be decremented between the BGP peers"; + } + + leaf multihop-ttl { + type uint8; + description + "Time-to-live value to use when packets are sent to the + referenced group or neighbors and ebgp-multihop is enabled"; + } + } + + grouping bgp-common-neighbor-group-route-reflector-config { + description + "Configuration parameters determining whether the behaviour of + the local system when acting as a route-reflector"; + + leaf route-reflector-cluster-id { + type oc-bgp-types:rr-cluster-id-type; + description + "route-reflector cluster id to use when local router is + configured as a route reflector. Commonly set at the group + level, but allows a different cluster + id to be set for each neighbor."; + } + + leaf route-reflector-client { + type boolean; + default "false"; + description + "Configure the neighbor as a route reflector client."; + } + } + + grouping bgp-common-neighbor-group-as-path-options-config { + description + "Configuration parameters allowing manipulation of the AS_PATH + attribute"; + + leaf allow-own-as { + type uint8; + default 0; + description + "Specify the number of occurrences of the local BGP speaker's + AS that can occur within the AS_PATH before it is rejected."; + } + + leaf replace-peer-as { + type boolean; + default "false"; + description + "Replace occurrences of the peer's AS in the AS_PATH + with the local autonomous system number"; + } + + leaf disable-peer-as-filter { + type boolean; + default "false"; + description + "When set to true, the system advertises routes to a peer + even if the peer's AS was in the AS path. The default + behavior (false) suppresses advertisements to peers if + their AS number is in the AS path of the route."; + } + } + + grouping bgp-common-neighbor-group-add-paths-config { + description + "Configuration parameters specfying whether the local system + will send or receive multiple paths using ADD_PATHS"; + + leaf receive { + type boolean; + default false; + description + "Enable capability negotiation to receive multiple path + advertisements for an NLRI from the neighbor or group"; + reference + "RFC 7911 - Advertisement of Multiple Paths in BGP"; + } + + leaf send { + type boolean; + default false; + description + "Enable capability negotiation to send multiple path + advertisements for an NLRI from the neighbor or group"; + reference + "RFC 7911 - Advertisement of Multiple Paths in BGP"; + } + + leaf send-max { + type uint8; + description + "The maximum total number of paths to advertise to neighbors + for a single NLRI. This includes the single best path as + well as additional paths advertised when add-paths is + enabled."; + } + + leaf eligible-prefix-policy { + type leafref { + path "/oc-rpol:routing-policy/oc-rpol:policy-definitions/" + + "oc-rpol:policy-definition/oc-rpol:name"; + } + description + "A reference to a routing policy which can be used to + restrict the prefixes for which add-paths is enabled"; + } + } + + grouping bgp-common-graceful-restart-config { + description + "Configuration parameters relating to BGP graceful restart."; + + leaf enabled { + type boolean; + description + "Enable or disable the graceful-restart capability."; + } + + leaf restart-time { + type uint16 { + range 0..4096; + } + description + "Estimated time (in seconds) for the local BGP speaker to + restart a session. This value is advertise in the graceful + restart BGP capability. This is a 12-bit value, referred to + as Restart Time in RFC4724. Per RFC4724, the suggested + default value is <= the hold-time value."; + } + + leaf stale-routes-time { + type decimal64 { + fraction-digits 2; + } + description + "An upper-bound on the time thate stale routes will be + retained by a router after a session is restarted. If an + End-of-RIB (EOR) marker is received prior to this timer + expiring stale-routes will be flushed upon its receipt - if + no EOR is received, then when this timer expires stale paths + will be purged. This timer is referred to as the + Selection_Deferral_Timer in RFC4724"; + } + + leaf helper-only { + type boolean; + description + "Enable graceful-restart in helper mode only. When this + leaf is set, the local system does not retain forwarding + its own state during a restart, but supports procedures + for the receiving speaker, as defined in RFC4724."; + } + } + + grouping bgp-common-use-multiple-paths-config { + description + "Generic configuration options relating to use of multiple + paths for a referenced AFI-SAFI, group or neighbor"; + + leaf enabled { + type boolean; + default false; + description + "Whether the use of multiple paths for the same NLRI is + enabled for the neighbor. This value is overridden by + any more specific configuration value."; + } + } + + grouping bgp-common-use-multiple-paths-ebgp-as-options-config { + description + "Configuration parameters specific to eBGP multipath applicable + to all contexts"; + + leaf allow-multiple-as { + type boolean; + default "false"; + description + "Allow multipath to use paths from different neighbouring + ASes. The default is to only consider multiple paths from + the same neighbouring AS."; + } + } + + grouping bgp-common-global-group-use-multiple-paths { + description + "Common grouping used for both global and groups which provides + configuration and state parameters relating to use of multiple + paths"; + + container use-multiple-paths { + description + "Parameters related to the use of multiple paths for the + same NLRI"; + + container config { + description + "Configuration parameters relating to multipath"; + uses bgp-common-use-multiple-paths-config; + } + container state { + config false; + description + "State parameters relating to multipath"; + uses bgp-common-use-multiple-paths-config; + } + + container ebgp { + description + "Multipath parameters for eBGP"; + container config { + description + "Configuration parameters relating to eBGP multipath"; + uses bgp-common-use-multiple-paths-ebgp-config; + } + container state { + config false; + description + "State information relating to eBGP multipath"; + uses bgp-common-use-multiple-paths-ebgp-config; + } + } + + container ibgp { + description + "Multipath parameters for iBGP"; + container config { + description + "Configuration parameters relating to iBGP multipath"; + uses bgp-common-use-multiple-paths-ibgp-config; + } + container state { + config false; + description + "State information relating to iBGP multipath"; + uses bgp-common-use-multiple-paths-ibgp-config; + } + } + } + } + + grouping bgp-common-use-multiple-paths-ebgp-config { + description + "Configuration parameters relating to multipath for eBGP"; + + leaf allow-multiple-as { + type boolean; + default "false"; + description + "Allow multipath to use paths from different neighbouring + ASes. The default is to only consider multiple paths from + the same neighbouring AS."; + } + + leaf maximum-paths { + type uint32; + default 1; + description + "Maximum number of parallel paths to consider when using + BGP multipath. The default is use a single path."; + } + } + + grouping bgp-common-use-multiple-paths-ibgp-config { + description + "Configuration parmaeters relating to multipath for iBGP"; + + leaf maximum-paths { + type uint32; + default 1; + description + "Maximum number of parallel paths to consider when using + iBGP multipath. The default is to use a single path"; + } + } + + grouping bgp-common-route-selection-options-config { + description + "Set of configuration options that govern best + path selection."; + + leaf always-compare-med { + type boolean; + default "false"; + description + "Compare multi-exit discriminator (MED) value from + different ASes when selecting the best route. The + default behavior is to only compare MEDs for paths + received from the same AS."; + } + + leaf ignore-as-path-length { + type boolean; + default "false"; + description + "Ignore the AS path length when selecting the best path. + The default is to use the AS path length and prefer paths + with shorter length."; + } + + leaf external-compare-router-id { + type boolean; + default "true"; + description + "When comparing similar routes received from external + BGP peers, use the router-id as a criterion to select + the active path."; + } + + leaf advertise-inactive-routes { + type boolean; + default "false"; + description + "Advertise inactive routes to external peers. The + default is to only advertise active routes."; + } + + leaf enable-aigp { + type boolean; + default false; + description + "Flag to enable sending / receiving accumulated IGP + attribute in routing updates"; + } + + leaf ignore-next-hop-igp-metric { + type boolean; + default "false"; + description + "Ignore the IGP metric to the next-hop when calculating + BGP best-path. The default is to select the route for + which the metric to the next-hop is lowest"; + } + } + + grouping bgp-common-route-selection-options { + description + "Configuration and state relating to route selection options"; + + container route-selection-options { + description + "Parameters relating to options for route selection"; + container config { + description + "Configuration parameters relating to route selection + options"; + uses bgp-common-route-selection-options-config; + } + container state { + config false; + description + "State information for the route selection options"; + uses bgp-common-route-selection-options-config; + } + } + } + + grouping bgp-common-state { + description + "Grouping containing common counters relating to prefixes and + paths"; + + leaf total-paths { + type uint32; + description + "Total number of BGP paths within the context"; + } + + leaf total-prefixes { + type uint32; + description + "Total number of BGP prefixes received within the context"; + } + } + + +} diff --git a/models/yang/common/openconfig-bgp-errors.yang b/models/yang/common/openconfig-bgp-errors.yang new file mode 100644 index 0000000000..86aad056e7 --- /dev/null +++ b/models/yang/common/openconfig-bgp-errors.yang @@ -0,0 +1,427 @@ +submodule openconfig-bgp-errors { + + belongs-to openconfig-bgp-types { + prefix "oc-bgp-types"; + } + + import openconfig-extensions { prefix "oc-ext"; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module defines BGP NOTIFICATION message error codes + and subcodes"; + + oc-ext:openconfig-version "5.0.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + identity BGP_ERROR_CODE { + description + "Indicates the error type in a BGP NOTIFICATION message"; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + identity BGP_ERROR_SUBCODE { + description + "Provides more specific information about the nature of the + error reported in a NOTIFICATION message. Each Error + Code may have one or more Error Subcodes associated with it."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + + identity UNSPECIFIC { + base BGP_ERROR_SUBCODE; + description + "The error subcode field is unspecific when the NOTIFICATION + message does not include any specific error subcode (i.e.., + value 0)."; + } + + identity MESSAGE_HEADER_ERROR { + base BGP_ERROR_CODE; + description + "Errors detected while processing the Message Header"; + } + + identity OPEN_MESSAGE_ERROR { + base BGP_ERROR_CODE; + description + "Errors detected while processing the OPEN message"; + } + + identity UPDATE_MESSAGE_ERROR { + base BGP_ERROR_CODE; + description + "Errors detected while processing the UPDATE message"; + } + + identity HOLD_TIMER_EXPIRED { + base BGP_ERROR_CODE; + description + "Indicates that the system did not receive successive + KEEPALIVE, UPDATE, and/or NOTIFICATION messages within the + period specified in the Hold Time field of the OPEN message"; + } + + identity FINITE_STATE_MACHINE_ERROR { + base BGP_ERROR_CODE; + description + "Error detected by the BGP Finite State Machine + (e.g., receipt of an unexpected event)"; + } + + identity CEASE { + base BGP_ERROR_CODE; + description + "Sent by a BGP peer to close its BGP connection in absence of + any fatal errors. If the BGP speaker terminates its + connection with a neihbor because the number of prefixes + received exceeds the configured upper bound, the speaker must + send the neighbor a NOTIFICATION message with the Cease + error code."; + } + + identity ROUTE_REFRESH_MESSAGE_ERROR { + base BGP_ERROR_CODE; + description + "The length, excluding the fixed-size message header, of the + received ROUTE-REFRESH message with Message Subtype 1 and 2 is + not 4. Applicable only when a BGP speaker has received the + 'Enhanced Route Refresh Capability' from a peer"; + reference + "RFC 7313 - Enhanced Route Refresh Capability for BGP-4"; + } + + identity MESSAGE_HEADER_SUBCODE { + base BGP_ERROR_SUBCODE; + description + "Error subcode definitions for Message Header error + notifications"; + } + + identity CONNECTION_NOT_SYNCHRONIZED { + base MESSAGE_HEADER_SUBCODE; + description + "Marker field of the message header is not all ones as + expected"; + } + + identity BAD_MESSAGE_LENGTH { + base MESSAGE_HEADER_SUBCODE; + description + "Indicates the message has an erroneous length with one + or more of the following: + + - the Length field of the message header is less than 19 or + greater than 4096 + + - the Length field of an OPEN message is less than the minimum + length of the OPEN message + + - the Length field of an UPDATE message is less than the + minimum length of the UPDATE message + + - the Length field of a KEEPALIVE message is not equal to 19 + + - the Length field of a NOTIFICATION message is less than the + minimum length of the NOTIFICATION message + + The erroneous Length field must be reported in the + NOTIFICATION data."; + } + + identity BAD_MESSAGE_TYPE { + base MESSAGE_HEADER_SUBCODE; + description + "Type field of the message header is not recognized. The + erroneous type field must be reported in the NOTIFICATION + data"; + } + + identity OPEN_MESSAGE_SUBCODE { + base BGP_ERROR_SUBCODE; + description + "Error subcode definitions for OPEN message error + notifications"; + } + + identity UNSUPPORTED_VERSION_NUMBER { + base OPEN_MESSAGE_SUBCODE; + description + "Version number in the Version field of the received OPEN + message is not supported"; + } + + identity BAD_PEER_AS { + base OPEN_MESSAGE_SUBCODE; + description + "Autonomous System field of the OPEN message is unacceptable"; + } + + identity BAD_BGP_IDENTIFIER { + base OPEN_MESSAGE_SUBCODE; + description + "BGP Identifier field of the OPEN message is syntactically + incorrect"; + } + + identity UNSUPPORTED_OPTIONAL_PARAMETER { + base OPEN_MESSAGE_SUBCODE; + description + "One of the Optional Parameters in the OPEN message is not + recognized"; + } + + identity UNACCEPTABLE_HOLD_TIME { + base OPEN_MESSAGE_SUBCODE; + description + "Hold Time field of the OPEN message is unacceptable"; + } + + identity UNSUPPORTED_CAPABILITY { + base OPEN_MESSAGE_SUBCODE; + description + "Inidicates that the peer does not support capabilities + advertisement -- the peer may send this subcode in response to + an OPEN message that carries the Capabilities Optional + Parameter"; + reference + "RFC 5492 - Capabilities Advertisement with BGP-4"; + } + + identity UPDATE_MESSAGE_SUBCODE { + base BGP_ERROR_SUBCODE; + description + "Error subcode definitions for UPDATE message error + notifications"; + } + + identity MALFORMED_ATTRIBUTE_LIST { + base UPDATE_MESSAGE_SUBCODE; + description + "Inidicates Withdrawn Routes Length or Total Attribute Length + is too large, or + + An attribute appears more than once in the UPDATE message"; + } + + identity UNRECOGNIZED_WELL_KNOWN_ATTRIBUTE { + base UPDATE_MESSAGE_SUBCODE; + description + "One or more of the well-known mandatory attributes are not + recognized"; + } + + identity MISSING_WELL_KNOWN_ATTRIBUTE { + base UPDATE_MESSAGE_SUBCODE; + description + "One or more of the well-known mandatory attributes are not + present"; + } + + identity ATTRIBUTE_FLAGS_ERROR { + base UPDATE_MESSAGE_SUBCODE; + description + "Attribute has Attribute Flags that conflict with the + Attribute Type Code"; + } + + identity ATTRIBUTE_LENGTH_ERROR { + base UPDATE_MESSAGE_SUBCODE; + description + "Attribute has an Attribute Length that conflicts with the + expected length (based on the attribute type code)"; + } + + identity INVALID_ORIGIN_ATTRIBUTE { + base UPDATE_MESSAGE_SUBCODE; + description + "ORIGIN attribute has an undefined value"; + } + + identity INVALID_NEXT_HOP_ATTRIBUTE { + base UPDATE_MESSAGE_SUBCODE; + description + "The NEXT_HOP attribute field is syntactically incorrect"; + } + + identity OPTIONAL_ATTRIBUTE_ERROR { + base UPDATE_MESSAGE_SUBCODE; + description + "An error is detected in the value of a recognized optional + attribute (such an attribute must be discarded)"; + } + + identity INVALID_NETWORK_FIELD { + base UPDATE_MESSAGE_SUBCODE; + description + "The NLRI field in the UPDATE message is syntactically + incorrect"; + } + + identity MALFORMED_AS_PATH { + base UPDATE_MESSAGE_SUBCODE; + description + "The AS_PATH attribute is syntactically incorrect"; + } + + identity FINITE_STATE_MACHINE_SUBCODE { + base BGP_ERROR_SUBCODE; + description + "Error subcode definitions for BGP finite state machine + errors."; + reference + "RFC 6608 - Subcodes for BGP Finite State Machine Error"; + } + + identity RECEIVE_UNEXPECTED_MESSAGE_OPENSENT { + base FINITE_STATE_MACHINE_SUBCODE; + description + "The peer BGP speaker received an unexpected message from + the local system while the peer speaker session was in the + OpenSent state"; + } + + identity RECEIVE_UNEXPECTED_MESSAGE_OPENCONFIRM { + base FINITE_STATE_MACHINE_SUBCODE; + description + "The peer BGP speaker received an unexpected message from + the local system while the peer speaker session was in the + OpenConfirm state"; + } + + identity RECEIVE_UNEXPECTED_MESSAGE_ESTABLISHED { + base FINITE_STATE_MACHINE_SUBCODE; + description + "The peer BGP speaker received an unexpected message from + the local system while the peer speaker session was in the + Established state"; + } + + identity CEASE_SUBCODE { + base BGP_ERROR_SUBCODE; + description + "Error subcode definitions for Cease notification messages"; + reference + "RFC 4486 - Subcodes for BGP Cease Notification Message"; + } + + identity MAX_NUM_PREFIXES_REACHED { + base CEASE_SUBCODE; + description + "The peer BGP speaker terminated its peering with the local + system because the number of address prefixes received + exceeds a locally configured upper bound"; + } + + identity ADMINISTRATIVE_SHUTDOWN { + base CEASE_SUBCODE; + description + "The peer BGP speaker administratively shut down its peering + with the local system"; + } + + identity PEER_DE_CONFIGURED { + base CEASE_SUBCODE; + description + "The peer BGP speaker de-configure the peering with the local + system"; + } + + identity ADMINISTRATIVE_RESET { + base CEASE_SUBCODE; + description + "The peer BGP speaker administratively reset the peering with + the local system"; + } + + identity CONNECTION_REJECTED { + base CEASE_SUBCODE; + description + "The peer BGP speaker disallowed the BGP connection to the + local system after the peer speaker accepted a transport + protocol connection"; + } + + identity OTHER_CONFIG_CHANGE { + base CEASE_SUBCODE; + description + "The peer BGP speaker administratively reset the peering with + the local sytem due to a configuration change that is not + covered by another subcode."; + } + + identity CONN_COLLISION_RESOLUTION { + base CEASE_SUBCODE; + description + "The peer BGP speaker sent a CEASE NOTIFICATION as a result of + the collision resolution procedure described in RFC 4271"; + } + + identity OUT_OF_RESOURCES { + base CEASE_SUBCODE; + description + "The peer BGP speaker ran out of resources (e.g., memory) and + reset the session with the local system"; + } + + identity ROUTE_REFRESH_SUBCODE { + base BGP_ERROR_SUBCODE; + description + "Error subcode definitions for the ROUTE-REFRESH message + error"; + } + + identity INVALID_MESSAGE_LENGTH { + base ROUTE_REFRESH_SUBCODE; + description + "The length, excluding the fixed-size message header, of the + received ROUTE-REFRESH message with Message Subtype 1 and 2 + is not 4"; + } +} diff --git a/models/yang/common/openconfig-bgp-global.yang b/models/yang/common/openconfig-bgp-global.yang new file mode 100644 index 0000000000..cb9a5e1065 --- /dev/null +++ b/models/yang/common/openconfig-bgp-global.yang @@ -0,0 +1,401 @@ +submodule openconfig-bgp-global { + + belongs-to openconfig-bgp { + prefix "oc-bgp"; + } + + import openconfig-extensions { prefix oc-ext; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-inet-types { prefix oc-inet; } + + // Include common submodules + include openconfig-bgp-common; + include openconfig-bgp-common-multiprotocol; + include openconfig-bgp-peer-group; + include openconfig-bgp-common-structure; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This sub-module contains groupings that are specific to the + global context of the OpenConfig BGP module"; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + grouping bgp-global-config { + description + "Global configuration options for the BGP router."; + + leaf as { + type oc-inet:as-number; + mandatory true; + description + "Local autonomous system number of the router. Uses + the 32-bit as-number type from the model in RFC 6991."; + } + + leaf router-id { + type oc-yang:dotted-quad; + description + "Router id of the router - an unsigned 32-bit integer + expressed in dotted quad notation."; + reference + "RFC4271 - A Border Gateway Protocol 4 (BGP-4), + Section 4.2"; + } + } + + grouping bgp-global-state { + description + "Operational state parameters for the BGP neighbor"; + + uses bgp-common-state; + } + + grouping bgp-global-default-route-distance-config { + description + "Configuration options relating to the administrative distance + (or preference) assigned to routes received from different + sources (external, internal, and local)."; + + leaf external-route-distance { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from external + BGP (eBGP)."; + } + leaf internal-route-distance { + type uint8 { + range "1..255"; + } + description + "Administrative distance for routes learned from internal + BGP (iBGP)."; + } + } + + grouping bgp-global-confederation-config { + description + "Configuration options specifying parameters when the local + router is within an autonomous system which is part of a BGP + confederation."; + + leaf identifier { + type oc-inet:as-number; + description + "Confederation identifier for the autonomous system. + Setting the identifier indicates that the local-AS is part + of a BGP confederation."; + } + + leaf-list member-as { + type oc-inet:as-number; + description + "Remote autonomous systems that are to be treated + as part of the local confederation."; + } + } + + grouping bgp-global-dynamic-neighbors { + description + "Grouping containing configuration relating to dynamic peers."; + + container dynamic-neighbor-prefixes { + description + "A list of IP prefixes from which the system should: + - Accept connections to the BGP daemon + - Dynamically configure a BGP neighbor corresponding to the + source address of the remote system, using the parameters + of the specified peer-group. + For such neighbors, an entry within the neighbor list should + be created, indicating that the peer was dynamically + configured, and referencing the peer-group from which the + configuration was derived."; + + list dynamic-neighbor-prefix { + key "prefix"; + description + "An individual prefix from which dynamic neighbor + connections are allowed."; + + leaf prefix { + type leafref { + path "../config/prefix"; + } + description + "Reference to the IP prefix from which source connections + are allowed for the dynamic neighbor group."; + } + + container config { + description + "Configuration parameters relating to the source prefix + for the dynamic BGP neighbor connections."; + + uses bgp-global-dynamic-neighbor-config; + } + + container state { + config false; + description + "Operational state parameters relating to the source + prefix for the dynamic BGP neighbor connections."; + + uses bgp-global-dynamic-neighbor-config; + } + } + } + } + + grouping bgp-global-dynamic-neighbor-config { + description + "Configuration parameters relating to an individual prefix from + which dynamic neighbors are accepted."; + + leaf prefix { + type oc-inet:ip-prefix; + description + "The IP prefix within which the source address of the remote + BGP speaker must fall to be considered eligible to the + dynamically configured."; } + + leaf peer-group { + type leafref { + // At bgp/global/dynamic-neighbor-prefixes/dynamic-neighbor + // prefix/config/peer-group + path "../../../../../peer-groups/peer-group/config/" + + "peer-group-name"; + } + description + "The peer-group within which the dynamic neighbor will be + configured. The configuration parameters used for the dynamic + neighbor are those specified within the referenced peer + group."; + } + } + + grouping bgp-global-mp-all-afi-safi-list-contents { + description + "A grouping used for contents of the list of AFI-SAFI + entries at the global BGP level."; + + // import and export policy included for the afi/safi + + uses bgp-common-mp-ipv4-unicast-group; + uses bgp-common-mp-ipv6-unicast-group; + uses bgp-common-mp-ipv4-labeled-unicast-group; + uses bgp-common-mp-ipv6-labeled-unicast-group; + uses bgp-common-mp-l3vpn-ipv4-unicast-group; + uses bgp-common-mp-l3vpn-ipv6-unicast-group; + uses bgp-common-mp-l3vpn-ipv4-multicast-group; + uses bgp-common-mp-l3vpn-ipv6-multicast-group; + uses bgp-common-mp-l2vpn-vpls-group; + uses bgp-common-mp-l2vpn-evpn-group; + uses bgp-common-mp-srte-policy-ipv4-group; + uses bgp-common-mp-srte-policy-ipv6-group; + } + + grouping bgp-global-afi-safi-list { + description + "List of address-families associated with the BGP instance"; + + list afi-safi { + key "afi-safi-name"; + + description + "AFI,SAFI configuration available for the + neighbour or group"; + + leaf afi-safi-name { + type leafref { + path "../config/afi-safi-name"; + } + description + "Reference to the AFI-SAFI name used as a key + for the AFI-SAFI list"; + } + + container config { + description + "Configuration parameters for the AFI-SAFI"; + uses bgp-common-mp-afi-safi-config; + } + container state { + config false; + description + "State information relating to the AFI-SAFI"; + uses bgp-common-mp-afi-safi-config; + uses bgp-common-state; + } + + container graceful-restart { + description + "Parameters relating to BGP graceful-restart"; + container config { + description + "Configuration options for BGP graceful-restart"; + uses bgp-common-mp-afi-safi-graceful-restart-config; + } + container state { + config false; + description + "State information for BGP graceful-restart"; + uses bgp-common-mp-afi-safi-graceful-restart-config; + } + } + + uses bgp-common-route-selection-options; + uses bgp-common-global-group-use-multiple-paths; + uses bgp-common-structure-neighbor-group-add-paths; + uses bgp-global-mp-all-afi-safi-list-contents; + } + } + + // Structural groupings + grouping bgp-global-base { + description + "Global configuration parameters for the BGP router"; + + container config { + description + "Configuration parameters relating to the global BGP router"; + uses bgp-global-config; + } + container state { + config false; + description + "State information relating to the global BGP router"; + uses bgp-global-config; + uses bgp-global-state; + } + + container default-route-distance { + description + "Administrative distance (or preference) assigned to + routes received from different sources + (external, internal, and local)."; + + container config { + description + "Configuration parameters relating to the default route + distance"; + uses bgp-global-default-route-distance-config; + } + container state { + config false; + description + "State information relating to the default route distance"; + uses bgp-global-default-route-distance-config; + } + } + + container confederation { + description + "Parameters indicating whether the local system acts as part + of a BGP confederation"; + + container config { + description + "Configuration parameters relating to BGP confederations"; + uses bgp-global-confederation-config; + } + container state { + config false; + description + "State information relating to the BGP confederations"; + uses bgp-global-confederation-config; + } + } + + container graceful-restart { + description + "Parameters relating the graceful restart mechanism for BGP"; + container config { + description + "Configuration parameters relating to graceful-restart"; + uses bgp-common-graceful-restart-config; + } + container state { + config false; + description + "State information associated with graceful-restart"; + uses bgp-common-graceful-restart-config; + } + } + + uses bgp-common-global-group-use-multiple-paths; + uses bgp-common-route-selection-options; + + container afi-safis { + description + "Address family specific configuration"; + uses bgp-global-afi-safi-list; + } + + uses bgp-global-dynamic-neighbors; + } + +} diff --git a/models/yang/common/openconfig-bgp-neighbor.yang b/models/yang/common/openconfig-bgp-neighbor.yang new file mode 100644 index 0000000000..8ec92f15f2 --- /dev/null +++ b/models/yang/common/openconfig-bgp-neighbor.yang @@ -0,0 +1,716 @@ +submodule openconfig-bgp-neighbor { + + belongs-to openconfig-bgp { + prefix "oc-bgp"; + } + + import openconfig-extensions { prefix oc-ext; } + import openconfig-routing-policy { prefix oc-rpol; } + import openconfig-types { prefix oc-types; } + import openconfig-bgp-types { prefix oc-bgp-types; } + import openconfig-inet-types { prefix oc-inet; } + import openconfig-yang-types { prefix oc-yang; } + + // Include the common submodule + include openconfig-bgp-common; + include openconfig-bgp-common-multiprotocol; + include openconfig-bgp-peer-group; + include openconfig-bgp-common-structure; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This sub-module contains groupings that are specific to the + neighbor context of the OpenConfig BGP module."; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + grouping bgp-neighbor-config { + description + "Configuration parameters relating to a base BGP neighbor that + are not also applicable to any other context + (e.g., peer group)"; + + leaf peer-group { + type leafref { + path "../../../../peer-groups/peer-group/peer-group-name"; + } + description + "The peer-group with which this neighbor is associated"; + } + + leaf neighbor-address { + type oc-inet:ip-address; + description + "Address of the BGP peer, either in IPv4 or IPv6"; + } + + leaf enabled { + type boolean; + default true; + description + "Whether the BGP peer is enabled. In cases where the + enabled leaf is set to false, the local system should not + initiate connections to the neighbor, and should not + respond to TCP connections attempts from the neighbor. If + the state of the BGP session is ESTABLISHED at the time + that this leaf is set to false, the BGP session should be + ceased."; + } + } + + grouping bgp-neighbor-use-multiple-paths { + description + "Multipath configuration and state applicable to a BGP + neighbor"; + + container use-multiple-paths { + description + "Parameters related to the use of multiple-paths for the same + NLRI when they are received only from this neighbor"; + + container config { + description + "Configuration parameters relating to multipath"; + uses bgp-common-use-multiple-paths-config; + } + container state { + config false; + description + "State parameters relating to multipath"; + uses bgp-common-use-multiple-paths-config; + } + + container ebgp { + description + "Multipath configuration for eBGP"; + container config { + description + "Configuration parameters relating to eBGP multipath"; + uses bgp-common-use-multiple-paths-ebgp-as-options-config; + } + container state { + config false; + description + "State information relating to eBGP multipath"; + uses bgp-common-use-multiple-paths-ebgp-as-options-config; + } + } + } + } + + grouping bgp-neighbor-state { + description + "Operational state parameters relating only to a BGP neighbor"; + + leaf session-state { + type enumeration { + enum IDLE { + description + "neighbor is down, and in the Idle state of the + FSM"; + } + enum CONNECT { + description + "neighbor is down, and the session is waiting for + the underlying transport session to be established"; + } + enum ACTIVE { + description + "neighbor is down, and the local system is awaiting + a conncetion from the remote peer"; + } + enum OPENSENT { + description + "neighbor is in the process of being established. + The local system has sent an OPEN message"; + } + enum OPENCONFIRM { + description + "neighbor is in the process of being established. + The local system is awaiting a NOTIFICATION or + KEEPALIVE message"; + } + enum ESTABLISHED { + description + "neighbor is up - the BGP session with the peer is + established"; + } + } + description + "Operational state of the BGP peer"; + } + + leaf last-established { + type oc-types:timeticks64; + description + "This timestamp indicates the time that the + BGP session last transitioned in or out of the Established + state. The value is the timestamp in seconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC). + + The BGP session uptime can be computed by clients as the + difference between this value and the current time in UTC + (assuming the session is in the ESTABLISHED state, per the + session-state leaf)."; + } + + leaf established-transitions { + type oc-yang:counter64; + description + "Number of transitions to the Established state for + the neighbor session. This value is analogous to the + bgpPeerFsmEstablishedTransitions object from the standard + BGP-4 MIB"; + reference + "RFC 4273 - Definitions of Managed Objects for BGP-4"; + } + + leaf-list supported-capabilities { + type identityref { + base oc-bgp-types:BGP_CAPABILITY; + } + description + "BGP capabilities negotiated as supported with the peer"; + } + + container messages { + description + "Counters for BGP messages sent and received from the + neighbor"; + container sent { + description + "Counters relating to BGP messages sent to the neighbor"; + uses bgp-neighbor-counters-message-types-state; + } + + container received { + description + "Counters for BGP messages received from the neighbor"; + uses bgp-neighbor-counters-message-types-state; + } + } + + container queues { + description + "Counters related to queued messages associated with the + BGP neighbor"; + uses bgp-neighbor-queue-counters-state; + } + + leaf dynamically-configured { + type boolean; + default false; + description + "When this leaf is set to true, the peer was configured dynamically + due to an inbound connection request from a specified source prefix + within a dynamic-neighbor-prefix."; + } + } + + grouping bgp-neighbor-counters-message-types-state { + description + "Grouping of BGP message types, included for re-use + across counters"; + + leaf UPDATE { + type uint64; + description + "Number of BGP UPDATE messages announcing, withdrawing + or modifying paths exchanged."; + } + + leaf NOTIFICATION { + type uint64; + description + "Number of BGP NOTIFICATION messages indicating an + error condition has occurred exchanged."; + } + + leaf last-notification-time { + type oc-types:timeticks64; + description + "This timestamp indicates the time that a NOTIFICATION + message was sent or received on the peering session + (based on whether this leaf is associated with + sent or received messages). + + The value is the timestamp in seconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + leaf last-notification-error-code { + type identityref { + base oc-bgp-types:BGP_ERROR_CODE; + } + description + "Indicates the last BGP error sent or received on the peering + session (based on whether this leaf is associated with + sent or received messages)."; + } + + leaf last-notification-error-subcode { + type identityref { + base oc-bgp-types:BGP_ERROR_SUBCODE; + } + description + "Indicates the last BGP error subcode sent or received on + the peering session (based on whether this leaf is associated + with sent or received messages)"; + } + } + + grouping bgp-neighbor-queue-counters-state { + description + "Counters relating to the message queues associated with the + BGP peer"; + + leaf input { + type uint32; + description + "The number of messages received from the peer currently + queued"; + } + + leaf output { + type uint32; + description + "The number of messages queued to be sent to the peer"; + } + } + + grouping bgp-neighbor-transport-state { + description + "Operational state parameters relating to the transport session + used for the BGP session"; + + leaf local-port { + type oc-inet:port-number; + description + "Local TCP port being used for the TCP session supporting + the BGP session"; + } + + leaf remote-address { + type oc-inet:ip-address; + description + "Remote address to which the BGP session has been + established"; + } + + leaf remote-port { + type oc-inet:port-number; + description + "Remote port being used by the peer for the TCP session + supporting the BGP session"; + } + } + + grouping bgp-neighbor-error-handling-state { + description + "Operational state parameters relating to enhanced error + error handling for BGP"; + + leaf erroneous-update-messages { + type uint32; + description + "The number of BGP UPDATE messages for which the + treat-as-withdraw mechanism has been applied based + on erroneous message contents"; + } + } + + grouping bgp-neighbor-timers-state { + description + "Operational state parameters relating to BGP timers associated + with the BGP session"; + + leaf negotiated-hold-time { + type decimal64 { + fraction-digits 2; + } + description + "The negotiated hold-time for the BGP session"; + } + } + + grouping bgp-neighbor-afi-safi-graceful-restart-state { + description + "Operational state variables relating to the graceful-restart + mechanism on a per-AFI-SAFI basis"; + + leaf received { + type boolean; + description + "This leaf indicates whether the neighbor advertised the + ability to support graceful-restart for this AFI-SAFI"; + } + + leaf advertised { + type boolean; + description + "This leaf indicates whether the ability to support + graceful-restart has been advertised to the peer"; + } + } + + grouping bgp-neighbor-graceful-restart-state { + description + "Operational state information relevant to graceful restart + for BGP"; + + leaf peer-restart-time { + type uint16 { + range 0..4096; + } + description + "The period of time (advertised by the peer) that + the peer expects a restart of a BGP session to + take"; + } + + leaf peer-restarting { + type boolean; + description + "This flag indicates whether the remote neighbor is currently + in the process of restarting, and hence received routes are + currently stale"; + } + + leaf local-restarting { + type boolean; + description + "This flag indicates whether the local neighbor is currently + restarting. The flag is unset after all NLRI have been + advertised to the peer, and the End-of-RIB (EOR) marker has + been unset"; + } + + leaf mode { + type enumeration { + enum HELPER_ONLY { + description + "The local router is operating in helper-only mode, and + hence will not retain forwarding state during a local + session restart, but will do so during a restart of the + remote peer"; + } + enum BILATERAL { + description + "The local router is operating in both helper mode, and + hence retains forwarding state during a remote restart, + and also maintains forwarding state during local session + restart"; + } + enum REMOTE_HELPER { + description + "The local system is able to retain routes during restart + but the remote system is only able to act as a helper"; + } + } + description + "Ths leaf indicates the mode of operation of BGP graceful + restart with the peer"; + } + } + + grouping bgp-neighbor-afi-safi-state { + description + "Operational state parameters relating to an individual AFI, + SAFI for a neighbor"; + + leaf active { + type boolean; + description + "This value indicates whether a particular AFI-SAFI has + been succesfully negotiated with the peer. An AFI-SAFI + may be enabled in the current running configuration, but a + session restart may be required in order to negotiate the new + capability."; + } + + container prefixes { + description "Prefix counters for the BGP session"; + leaf received { + type uint32; + description + "The number of prefixes received from the neighbor"; + } + + leaf sent { + type uint32; + description + "The number of prefixes advertised to the neighbor"; + } + + leaf installed { + type uint32; + description + "The number of advertised prefixes installed in the + Loc-RIB"; + } + } + } + + grouping bgp-neighbor-afi-safi-list { + description + "List of address-families associated with the BGP neighbor"; + + list afi-safi { + key "afi-safi-name"; + + description + "AFI,SAFI configuration available for the + neighbour or group"; + + + leaf afi-safi-name { + type leafref { + path "../config/afi-safi-name"; + } + description + "Reference to the AFI-SAFI name used as a key + for the AFI-SAFI list"; + } + + container config { + description + "Configuration parameters for the AFI-SAFI"; + uses bgp-common-mp-afi-safi-config; + } + container state { + config false; + description + "State information relating to the AFI-SAFI"; + uses bgp-common-mp-afi-safi-config; + uses bgp-neighbor-afi-safi-state; + } + + + container graceful-restart { + description + "Parameters relating to BGP graceful-restart"; + container config { + description + "Configuration options for BGP graceful-restart"; + uses bgp-common-mp-afi-safi-graceful-restart-config; + } + container state { + config false; + description + "State information for BGP graceful-restart"; + uses bgp-common-mp-afi-safi-graceful-restart-config; + uses bgp-neighbor-afi-safi-graceful-restart-state; + } + } + + uses bgp-common-structure-neighbor-group-add-paths; + uses bgp-common-mp-all-afi-safi-list-contents; + uses bgp-neighbor-use-multiple-paths; + } + } + + grouping bgp-neighbor-base { + description + "Parameters related to a BGP neighbor"; + + container config { + description + "Configuration parameters relating to the BGP neighbor or + group"; + uses bgp-neighbor-config; + uses bgp-common-neighbor-group-config; + } + container state { + config false; + description + "State information relating to the BGP neighbor"; + uses bgp-neighbor-config; + uses bgp-common-neighbor-group-config; + uses bgp-neighbor-state; + } + + container timers { + description + "Timers related to a BGP neighbor"; + container config { + description + "Configuration parameters relating to timers used for the + BGP neighbor"; + uses bgp-common-neighbor-group-timers-config; + } + container state { + config false; + description + "State information relating to the timers used for the BGP + neighbor"; + uses bgp-common-neighbor-group-timers-config; + uses bgp-neighbor-timers-state; + } + } + + container transport { + description + "Transport session parameters for the BGP neighbor"; + container config { + description + "Configuration parameters relating to the transport + session(s) used for the BGP neighbor"; + uses bgp-common-neighbor-group-transport-config; + } + container state { + config false; + description + "State information relating to the transport session(s) + used for the BGP neighbor"; + uses bgp-common-neighbor-group-transport-config; + uses bgp-neighbor-transport-state; + } + } + + container error-handling { + description + "Error handling parameters used for the BGP neighbor or + group"; + container config { + description + "Configuration parameters enabling or modifying the + behavior or enhanced error handling mechanisms for the BGP + neighbor"; + uses bgp-common-neighbor-group-error-handling-config; + } + container state { + config false; + description + "State information relating to enhanced error handling + mechanisms for the BGP neighbor"; + uses bgp-common-neighbor-group-error-handling-config; + uses bgp-neighbor-error-handling-state; + } + } + + container graceful-restart { + description + "Parameters relating the graceful restart mechanism for BGP"; + container config { + description + "Configuration parameters relating to graceful-restart"; + uses bgp-common-graceful-restart-config; + } + container state { + config false; + description + "State information associated with graceful-restart"; + uses bgp-common-graceful-restart-config; + uses bgp-neighbor-graceful-restart-state; + } + } + + uses bgp-common-structure-neighbor-group-logging-options; + uses bgp-common-structure-neighbor-group-ebgp-multihop; + uses bgp-common-structure-neighbor-group-route-reflector; + uses bgp-common-structure-neighbor-group-as-path-options; + uses bgp-neighbor-use-multiple-paths; + uses oc-rpol:apply-policy-group; + + container afi-safis { + description + "Per-address-family configuration parameters associated with + the neighbor"; + uses bgp-neighbor-afi-safi-list; + } + } + + grouping bgp-neighbor-list { + description + "The list of BGP neighbors"; + + list neighbor { + key "neighbor-address"; + description + "List of BGP neighbors configured on the local system, + uniquely identified by peer IPv[46] address"; + + leaf neighbor-address { + type leafref { + path "../config/neighbor-address"; + } + description + "Reference to the address of the BGP neighbor used as + a key in the neighbor list"; + } + + uses bgp-neighbor-base; + } + + } + + +} diff --git a/models/yang/common/openconfig-bgp-peer-group.yang b/models/yang/common/openconfig-bgp-peer-group.yang new file mode 100644 index 0000000000..918d8145f2 --- /dev/null +++ b/models/yang/common/openconfig-bgp-peer-group.yang @@ -0,0 +1,277 @@ +submodule openconfig-bgp-peer-group { + + belongs-to openconfig-bgp { + prefix "oc-bgp"; + } + + import openconfig-extensions { prefix oc-ext; } + import openconfig-routing-policy { prefix oc-rpol; } + + // Include the common submodule + include openconfig-bgp-common; + include openconfig-bgp-common-multiprotocol; + include openconfig-bgp-common-structure; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This sub-module contains groupings that are specific to the + peer-group context of the OpenConfig BGP module."; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + grouping bgp-peer-group-config { + description + "Configuration parameters relating to a base BGP peer group that + are not also applicable to any other context (e.g., neighbor)"; + + leaf peer-group-name { + type string; + description + "Name of the BGP peer-group"; + } + + } + + grouping bgp-peer-group-afi-safi-list { + description + "List of address-families associated with the BGP peer-group"; + + list afi-safi { + key "afi-safi-name"; + + description + "AFI,SAFI configuration available for the + neighbour or group"; + + leaf afi-safi-name { + type leafref { + path "../config/afi-safi-name"; + } + description + "Reference to the AFI-SAFI name used as a key + for the AFI-SAFI list"; + } + + container config { + description + "Configuration parameters for the AFI-SAFI"; + uses bgp-common-mp-afi-safi-config; + } + container state { + config false; + description + "State information relating to the AFI-SAFI"; + uses bgp-common-mp-afi-safi-config; + } + + container graceful-restart { + description + "Parameters relating to BGP graceful-restart"; + container config { + description + "Configuration options for BGP graceful-restart"; + uses bgp-common-mp-afi-safi-graceful-restart-config; + } + container state { + config false; + description + "State information for BGP graceful-restart"; + uses bgp-common-mp-afi-safi-graceful-restart-config; + } + } + + uses bgp-common-structure-neighbor-group-add-paths; + uses bgp-common-global-group-use-multiple-paths; + uses bgp-common-mp-all-afi-safi-list-contents; + } + } + + grouping bgp-peer-group-base { + description + "Parameters related to a BGP group"; + + container config { + description + "Configuration parameters relating to the BGP neighbor or + group"; + uses bgp-peer-group-config; + uses bgp-common-neighbor-group-config; + } + container state { + config false; + description + "State information relating to the BGP peer-group"; + uses bgp-peer-group-config; + uses bgp-common-neighbor-group-config; + uses bgp-common-state; + } + + container timers { + description + "Timers related to a BGP peer-group"; + + container config { + description + "Configuration parameters relating to timers used for the + BGP neighbor or peer group"; + uses bgp-common-neighbor-group-timers-config; + } + container state { + config false; + description + "State information relating to the timers used for the BGP + group"; + uses bgp-common-neighbor-group-timers-config; + } + } + + container transport { + description + "Transport session parameters for the BGP peer-group"; + + container config { + description + "Configuration parameters relating to the transport + session(s) used for the BGP neighbor or group"; + uses bgp-common-neighbor-group-transport-config; + } + container state { + config false; + description + "State information relating to the transport session(s) + used for the BGP neighbor or group"; + uses bgp-common-neighbor-group-transport-config; + } + } + + container error-handling { + description + "Error handling parameters used for the BGP peer-group"; + + container config { + description + "Configuration parameters enabling or modifying the + behavior or enhanced error handling mechanisms for the BGP + group"; + uses bgp-common-neighbor-group-error-handling-config; + } + container state { + config false; + description + "State information relating to enhanced error handling + mechanisms for the BGP group"; + uses bgp-common-neighbor-group-error-handling-config; + } + } + + container graceful-restart { + description + "Parameters relating the graceful restart mechanism for BGP"; + container config { + description + "Configuration parameters relating to graceful-restart"; + uses bgp-common-graceful-restart-config; + } + container state { + config false; + description + "State information associated with graceful-restart"; + uses bgp-common-graceful-restart-config; + } + } + + uses bgp-common-structure-neighbor-group-logging-options; + uses bgp-common-structure-neighbor-group-ebgp-multihop; + uses bgp-common-structure-neighbor-group-route-reflector; + uses bgp-common-structure-neighbor-group-as-path-options; + uses bgp-common-global-group-use-multiple-paths; + uses oc-rpol:apply-policy-group; + + container afi-safis { + description + "Per-address-family configuration parameters associated with + thegroup"; + uses bgp-peer-group-afi-safi-list; + } + } + + grouping bgp-peer-group-list { + description + "The list of BGP peer groups"; + + list peer-group { + key "peer-group-name"; + description + "List of BGP peer-groups configured on the local system - + uniquely identified by peer-group name"; + + leaf peer-group-name { + type leafref { + path "../config/peer-group-name"; + } + description + "Reference to the name of the BGP peer-group used as a + key in the peer-group list"; + } + + uses bgp-peer-group-base; + } + } + +} diff --git a/models/yang/common/openconfig-bgp-types.yang b/models/yang/common/openconfig-bgp-types.yang new file mode 100644 index 0000000000..cbf3b40255 --- /dev/null +++ b/models/yang/common/openconfig-bgp-types.yang @@ -0,0 +1,614 @@ +module openconfig-bgp-types { + yang-version "1"; + + namespace "http://openconfig.net/yang/bgp-types"; + + prefix "oc-bgp-types"; + + import openconfig-types { prefix "oc-types"; } + import openconfig-inet-types { prefix "oc-inet"; } + import openconfig-extensions { prefix "oc-ext"; } + + // Include definitions of BGP error notifications + include openconfig-bgp-errors; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module contains general data definitions for use in BGP + policy. It can be imported by modules that make use of BGP + attributes"; + + oc-ext:openconfig-version "5.0.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2018-03-20" { + description + "Added color extended community"; + reference "4.0.2"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + identity BGP_CAPABILITY { + description "Base identity for a BGP capability"; + } + + identity MPBGP { + base BGP_CAPABILITY; + description + "Multi-protocol extensions to BGP"; + reference "RFC2858"; + } + + identity ROUTE_REFRESH { + base BGP_CAPABILITY; + description + "The BGP route-refresh functionality"; + reference "RFC2918"; + } + + identity ASN32 { + base BGP_CAPABILITY; + description + "4-byte (32-bit) AS number functionality"; + reference "RFC6793"; + } + + identity GRACEFUL_RESTART { + base BGP_CAPABILITY; + description + "Graceful restart functionality"; + reference "RFC4724"; + } + + identity ADD_PATHS { + base BGP_CAPABILITY; + description + "BGP add-paths"; + reference "draft-ietf-idr-add-paths"; + } + + identity AFI_SAFI_TYPE { + description + "Base identity type for AFI,SAFI tuples for BGP-4"; + reference "RFC4760 - multiprotocol extensions for BGP-4"; + } + + identity IPV4_UNICAST { + base AFI_SAFI_TYPE; + description + "IPv4 unicast (AFI,SAFI = 1,1)"; + reference "RFC4760"; + } + + identity IPV6_UNICAST { + base AFI_SAFI_TYPE; + description + "IPv6 unicast (AFI,SAFI = 2,1)"; + reference "RFC4760"; + } + + identity IPV4_LABELED_UNICAST { + base AFI_SAFI_TYPE; + description + "Labeled IPv4 unicast (AFI,SAFI = 1,4)"; + reference "RFC3107"; + } + + identity IPV6_LABELED_UNICAST { + base AFI_SAFI_TYPE; + description + "Labeled IPv6 unicast (AFI,SAFI = 2,4)"; + reference "RFC3107"; + } + + identity L3VPN_IPV4_UNICAST { + base AFI_SAFI_TYPE; + description + "Unicast IPv4 MPLS L3VPN (AFI,SAFI = 1,128)"; + reference "RFC4364"; + } + + identity L3VPN_IPV6_UNICAST { + base AFI_SAFI_TYPE; + description + "Unicast IPv6 MPLS L3VPN (AFI,SAFI = 2,128)"; + reference "RFC4659"; + } + + identity L3VPN_IPV4_MULTICAST { + base AFI_SAFI_TYPE; + description + "Multicast IPv4 MPLS L3VPN (AFI,SAFI = 1,129)"; + reference "RFC6514"; + } + + identity L3VPN_IPV6_MULTICAST { + base AFI_SAFI_TYPE; + description + "Multicast IPv6 MPLS L3VPN (AFI,SAFI = 2,129)"; + reference "RFC6514"; + } + + identity L2VPN_VPLS { + base AFI_SAFI_TYPE; + description + "BGP-signalled VPLS (AFI,SAFI = 25,65)"; + reference "RFC4761"; + } + + identity L2VPN_EVPN { + base AFI_SAFI_TYPE; + description + "BGP MPLS Based Ethernet VPN (AFI,SAFI = 25,70)"; + } + + identity SRTE_POLICY_IPV4 { + base AFI_SAFI_TYPE; + description + "Segment Routing Traffic Engineering (SRTE) Policy + for IPv4 (AFI,SAFI = 1,73)"; + } + + identity SRTE_POLICY_IPV6 { + base AFI_SAFI_TYPE; + description + "Segment Routing Traffic Engineering (SRTE) Policy + for IPv6 (AFI,SAFI = 2,73)"; + } + + identity BGP_WELL_KNOWN_STD_COMMUNITY { + description + "Reserved communities within the standard community space + defined by RFC1997. These communities must fall within the + range 0x00000000 to 0xFFFFFFFF"; + reference "RFC1997"; + } + + identity NO_EXPORT { + base BGP_WELL_KNOWN_STD_COMMUNITY; + description + "Do not export NLRI received carrying this community outside + the bounds of this autonomous system, or this confederation if + the local autonomous system is a confederation member AS. This + community has a value of 0xFFFFFF01."; + reference "RFC1997"; + } + + identity NO_ADVERTISE { + base BGP_WELL_KNOWN_STD_COMMUNITY; + description + "All NLRI received carrying this community must not be + advertised to other BGP peers. This community has a value of + 0xFFFFFF02."; + reference "RFC1997"; + } + + identity NO_EXPORT_SUBCONFED { + base BGP_WELL_KNOWN_STD_COMMUNITY; + description + "All NLRI received carrying this community must not be + advertised to external BGP peers - including over confederation + sub-AS boundaries. This community has a value of 0xFFFFFF03."; + reference "RFC1997"; + } + + identity NOPEER { + base BGP_WELL_KNOWN_STD_COMMUNITY; + description + "An autonomous system receiving NLRI tagged with this community + is advised not to readvertise the NLRI to external bi-lateral + peer autonomous systems. An AS may also filter received NLRI + from bilateral peer sessions when they are tagged with this + community value"; + reference "RFC3765"; + } + + typedef bgp-session-direction { + type enumeration { + enum INBOUND { + description + "Refers to all NLRI received from the BGP peer"; + } + enum OUTBOUND { + description + "Refers to all NLRI advertised to the BGP peer"; + } + } + description + "Type to describe the direction of NLRI transmission"; + } + + typedef bgp-well-known-community-type { + type identityref { + base BGP_WELL_KNOWN_STD_COMMUNITY; + } + description + "Type definition for well-known IETF community attribute + values"; + reference + "IANA Border Gateway Protocol (BGP) Well Known Communities"; + } + + + typedef bgp-std-community-type { + // TODO: further refine restrictions and allowed patterns + // 4-octet value: + // 2 octets + // 2 octets + type union { + type uint32 { + // per RFC 1997, 0x00000000 - 0x0000FFFF and 0xFFFF0000 - + // 0xFFFFFFFF are reserved + } + type string { + pattern '^(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + } + description + "Type definition for standard commmunity attributes represented as + a integer value, or a string of the form N:M where N and M are + integers between 0 and 65535."; + reference "RFC 1997 - BGP Communities Attribute"; + } + + typedef bgp-ext-community-type { + type union { + type string { + // Type 1: 2-octet global and 4-octet local + // (AS number) (Integer) + pattern '^(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9]):' + + '(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9])$'; + } + type string { + // Type 2: 4-octet global and 2-octet local + // (ipv4-address) (integer) + 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]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + type string { + // RFC5668: 4-octet global and 2-octet local + // (AS number) (integer) + pattern '^(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + type string { + // route-target with Type 1 + // route-target:(ASN):(local-part) + pattern '^route\-target:' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9]):' + + '(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9])$'; + } + type string { + // route-target with Type 2 + // route-target:(IPv4):(local-part) + pattern '^route\-target:' + + '(([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]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + type string { + // 4-byte AS Type 1 route-target + pattern '^route\-target:' + + '(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + type string { + // route-origin with Type 1 + pattern '^route\-origin:' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9]):' + + '(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9])$'; + } + type string { + // route-origin with Type 2 + pattern '^route\-origin:' + + '(([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]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + type string { + // 4-byte AS Type 1 route-origin + pattern '^route\-origin:' + + '(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9]):' + + '(6553[0-5]|655[0-2][0-9]|654[0-9]{2}|65[0-4][0-9]{2}' + + '|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{1,3}|[0-9])$'; + } + type string { + // Extended Color Community + pattern '^color:' + + '[0-1]{2}:' + + '(429496729[0-5]|42949672[0-8][0-9]|4294967[0-1][0-9]{2}' + + '|429496[0-6][0-9]{3}|42949[0-5][0-9]{4}|4294[0-8][0-9]{5}|' + + '429[0-3][0-9]{6}|4[0-1][0-9]{7}|[1-3][0-9]{9}|' + + '[1-9][0-9]{1,8}|[0-9])$'; + } + } + description + "Type definition for extended community attributes. In the case that + common communities are utilised, they are represented as a string + of the form: + - <2b AS>:<4b value> per RFC4360 section 3.1 + - <4b IPv4>:<2b value> per RFC4360 section 3.2 + - <4b AS>:<2b value> per RFC5668 section 2. + - route-target:<2b AS>:<4b value> per RFC4360 section 4 + - route-target:<4b IPv4>:<2b value> per RFC4360 section 4 + - route-origin:<2b ASN>:<4b value> per RFC4360 section 5 + - route-origin:<4b IPv4>:<2b value> per RFC4360 section 5 + - color::<4b value> per draft-ietf-idr-segment-routing-te-policy + section 3"; + reference + "RFC 4360 - BGP Extended Communities Attribute + RFC 5668 - 4-Octet AS Specific BGP Extended Community + draft-ietf-idr-segment-routing-te-policy"; + } + + typedef bgp-ext-community-recv-type { + type union { + type bgp-ext-community-type; + type binary { + length 8; + } + } + description + "A type definition utilised to define the extended community + in a context where the system is receiving the extended + community from an external source, such that the value may be + unknown. In the case that the received extended community is + unknown it is defined to be a 8-octet quantity formatted + according to RFC4360: + + Type Field: 1 or 2 octets. + Value Field: Remaining octets. + + The high-order octet of the type field is encoded such that + bit 0 indicates whether the extended community type is IANA + assigned; and bit 1 indicates whether the extended community + is transitive. The remaining bits of the high-order type + field must be interpreted to determine whether the low-order + type field should be parsed, or whether the entire remainder + of the extended community is a value."; + reference + "RFC 4360 - BGP Extended Communities Attribute + RFC 5668 - 4-Octet AS Specific BGP Extended Community"; + } + + typedef bgp-community-regexp-type { + // TODO: needs more work to decide what format these regexps can + // take. + type oc-types:std-regexp; + description + "Type definition for communities specified as regular + expression patterns"; + } + + typedef bgp-origin-attr-type { + type enumeration { + enum IGP { + description + "Origin of the NLRI is internal"; + } + enum EGP { + description + "Origin of the NLRI is EGP"; + } + enum INCOMPLETE { + description + "Origin of the NLRI is neither IGP or EGP"; + } + } + description + "Type definition for standard BGP origin attribute"; + reference "RFC 4271 - A Border Gateway Protocol 4 (BGP-4), + Sec 4.3"; + } + + typedef peer-type { + type enumeration { + enum INTERNAL { + description + "Internal (iBGP) peer"; + } + enum EXTERNAL { + description + "External (eBGP) peer"; + } + } + description + "Labels a peer or peer group as explicitly internal or + external"; + } + + identity REMOVE_PRIVATE_AS_OPTION { + description + "Base identity for options for removing private autonomous + system numbers from the AS_PATH attribute"; + } + + identity PRIVATE_AS_REMOVE_ALL { + base REMOVE_PRIVATE_AS_OPTION; + description + "Strip all private autonmous system numbers from the AS_PATH. + This action is performed regardless of the other content of the + AS_PATH attribute, and for all instances of private AS numbers + within that attribute."; + } + + identity PRIVATE_AS_REPLACE_ALL { + base REMOVE_PRIVATE_AS_OPTION; + description + "Replace all instances of private autonomous system numbers in + the AS_PATH with the local BGP speaker's autonomous system + number. This action is performed regardless of the other + content of the AS_PATH attribute, and for all instances of + private AS number within that attribute."; + } + + typedef remove-private-as-option { + type identityref { + base REMOVE_PRIVATE_AS_OPTION; + } + description + "Set of options for configuring how private AS path numbers + are removed from advertisements"; + } + + typedef rr-cluster-id-type { + type union { + type uint32; + type oc-inet:ipv4-address; + } + description + "Union type for route reflector cluster ids: + option 1: 4-byte number + option 2: IP address"; + } + + typedef community-type { + type enumeration { + enum STANDARD { + description "Send only standard communities"; + } + enum EXTENDED { + description "Send only extended communities"; + } + enum BOTH { + description "Send both standard and extended communities"; + } + enum NONE { + description "Do not send any community attribute"; + } + } + description + "type describing variations of community attributes: + STANDARD: standard BGP community [rfc1997] + EXTENDED: extended BGP community [rfc4360] + BOTH: both standard and extended community"; + } + + + typedef as-path-segment-type { + type enumeration { + enum AS_SEQ { + description + "Ordered set of autonomous systems that a route in + the UPDATE message has traversed"; + } + enum AS_SET { + description + "Unordered set of autonomous systems that a route in + the UPDATE message has traversed"; + } + enum AS_CONFED_SEQUENCE { + description + "Ordered set of Member Autonomous + Systems in the local confederation that the UPDATE message + has traversed"; + } + enum AS_CONFED_SET { + description + "Unordered set of Member Autonomous Systems + in the local confederation that the UPDATE message has + traversed"; + } + } + description + "Defines the types of BGP AS path segments."; + } +} diff --git a/models/yang/common/openconfig-bgp.yang b/models/yang/common/openconfig-bgp.yang new file mode 100644 index 0000000000..43f688706b --- /dev/null +++ b/models/yang/common/openconfig-bgp.yang @@ -0,0 +1,175 @@ +module openconfig-bgp { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/bgp"; + + prefix "oc-bgp"; + + // import some basic inet types + import openconfig-extensions { prefix oc-ext; } + import openconfig-rib-bgp { prefix oc-bgprib; } + + // Include the OpenConfig BGP submodules + // Common: defines the groupings that are common across more than + // one context (where contexts are neighbor, group, global) + include openconfig-bgp-common; + // Multiprotocol: defines the groupings that are common across more + // than one context, and relate to Multiprotocol + include openconfig-bgp-common-multiprotocol; + // Structure: defines groupings that are shared but are solely used for + // structural reasons. + include openconfig-bgp-common-structure; + // Include peer-group/neighbor/global - these define the groupings + // that are specific to one context + include openconfig-bgp-peer-group; + include openconfig-bgp-neighbor; + include openconfig-bgp-global; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module describes a YANG model for BGP protocol + configuration.It is a limited subset of all of the configuration + parameters available in the variety of vendor implementations, + hence it is expected that it would be augmented with vendor- + specific configuration data as needed. Additional modules or + submodules to handle other aspects of BGP configuration, + including policy, VRFs, VPNs, and additional address families + are also expected. + + This model supports the following BGP configuration level + hierarchy: + + BGP + | + +-> [ global BGP configuration ] + +-> AFI / SAFI global + +-> peer group + +-> [ peer group config ] + +-> AFI / SAFI [ per-AFI overrides ] + +-> neighbor + +-> [ neighbor config ] + +-> [ optional pointer to peer-group ] + +-> AFI / SAFI [ per-AFI overrides ]"; + + oc-ext:openconfig-version "5.1.0"; + + revision "2019-04-16" { + description + "Add BGP RIB to the top-level BGP container"; + reference "5.1.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "5.0.2"; + } + + revision "2018-08-20" { + description + "Correct description of AFI-SAFI enabled leaf."; + reference "5.0.1"; + } + + revision "2018-04-11" { + description + "Correct naming of BGP maximum prefix warning percentage leaf."; + reference "5.0.0"; + } + + revision "2018-03-20" { + description + "Added SR-TE policy SAFI"; + reference "4.1.0"; + } + + revision "2017-07-30" { + description + "Clarification of add-paths send-max leaf"; + reference "4.0.1"; + } + + revision "2017-07-10" { + description + "Add error notifications; moved add-paths config; add AS + prepend policy features; removed unneeded config leaves"; + reference "4.0.0"; + } + + revision "2017-02-02" { + description + "Bugfix to remove remaining global-level policy data"; + reference "3.0.1"; + } + + revision "2017-01-26" { + description + "Add dynamic neighbor support, migrate to OpenConfig types"; + reference "3.0.0"; + } + + revision "2016-06-21" { + description + "OpenConfig BGP refactor"; + reference "2.1.1"; + } + + revision "2016-06-06" { + description + "OpenConfig public release"; + reference "2.1.0"; + } + + revision "2016-03-31" { + description + "OpenConfig public release"; + reference "2.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping bgp-top { + description + "Top-level grouping for the BGP model data"; + + container bgp { + description + "Top-level configuration and state for the BGP router"; + + container global { + description + "Global configuration for the BGP router"; + uses bgp-global-base; + } + + container neighbors { + description + "Configuration for BGP neighbors"; + uses bgp-neighbor-list; + } + + container peer-groups { + description + "Configuration for BGP peer-groups"; + uses bgp-peer-group-list; + } + + uses oc-bgprib:bgp-rib-top; + } + } + + uses bgp-top; + +} diff --git a/models/yang/common/openconfig-extensions.yang b/models/yang/common/openconfig-extensions.yang new file mode 100644 index 0000000000..361e55426d --- /dev/null +++ b/models/yang/common/openconfig-extensions.yang @@ -0,0 +1,175 @@ +module openconfig-extensions { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/openconfig-ext"; + + prefix "oc-ext"; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module provides extensions to the YANG language to allow + OpenConfig specific functionality and meta-data to be defined."; + + revision "2018-10-17" { + description + "Add extension for regular expression type."; + reference "0.4.0"; + } + + revision "2017-04-11" { + description + "rename password type to 'hashed' and clarify description"; + reference "0.3.0"; + } + + revision "2017-01-29" { + description + "Added extension for annotating encrypted values."; + reference "0.2.0"; + } + + revision "2015-10-09" { + description + "Initial OpenConfig public release"; + reference "0.1.0"; + } + + + // extension statements + extension openconfig-version { + argument "semver" { + yin-element false; + } + description + "The OpenConfig version number for the module. This is + expressed as a semantic version number of the form: + x.y.z + where: + * x corresponds to the major version, + * y corresponds to a minor version, + * z corresponds to a patch version. + This version corresponds to the model file within which it is + defined, and does not cover the whole set of OpenConfig models. + Where several modules are used to build up a single block of + functionality, the same module version is specified across each + file that makes up the module. + + A major version number of 0 indicates that this model is still + in development (whether within OpenConfig or with industry + partners), and is potentially subject to change. + + Following a release of major version 1, all modules will + increment major revision number where backwards incompatible + changes to the model are made. + + The minor version is changed when features are added to the + model that do not impact current clients use of the model. + + The patch-level version is incremented when non-feature changes + (such as bugfixes or clarifications to human-readable + descriptions that do not impact model functionality) are made + that maintain backwards compatibility. + + The version number is stored in the module meta-data."; + } + + extension openconfig-hashed-value { + description + "This extension provides an annotation on schema nodes to + indicate that the corresponding value should be stored and + reported in hashed form. + + Hash algorithms are by definition not reversible. Clients + reading the configuration or applied configuration for the node + should expect to receive only the hashed value. Values written + in cleartext will be hashed. This annotation may be used on + nodes such as secure passwords in which the device never reports + a cleartext value, even if the input is provided as cleartext."; + } + + extension regexp-posix { + description + "This extension indicates that the regular expressions included + within the YANG module specified are conformant with the POSIX + regular expression format rather than the W3C standard that is + specified by RFC6020 and RFC7950."; + } + + extension telemetry-on-change { + description + "The telemetry-on-change annotation is specified in the context + of a particular subtree (container, or list) or leaf within the + YANG schema. Where specified, it indicates that the value stored + by the nodes within the context change their value only in response + to an event occurring. The event may be local to the target, for + example - a configuration change, or external - such as the failure + of a link. + + When a telemetry subscription allows the target to determine whether + to export the value of a leaf in a periodic or event-based fashion + (e.g., TARGET_DEFINED mode in gNMI), leaves marked as + telemetry-on-change should only be exported when they change, + i.e., event-based."; + } + + extension telemetry-atomic { + description + "The telemetry-atomic annotation is specified in the context of + a subtree (containre, or list), and indicates that all nodes + within the subtree are always updated together within the data + model. For example, all elements under the subtree may be updated + as a result of a new alarm being raised, or the arrival of a new + protocol message. + + Transport protocols may use the atomic specification to determine + optimisations for sending or storing the corresponding data."; + } + + extension operational { + description + "The operational annotation is specified in the context of a + grouping, leaf, or leaf-list within a YANG module. It indicates + that the nodes within the context are derived state on the device. + + OpenConfig data models divide nodes into the following three categories: + + - intended configuration - these are leaves within a container named + 'config', and are the writable configuration of a target. + - applied configuration - these are leaves within a container named + 'state' and are the currently running value of the intended configuration. + - derived state - these are the values within the 'state' container which + are not part of the applied configuration of the device. Typically, they + represent state values reflecting underlying operational counters, or + protocol statuses."; + } + + extension catalog-organization { + argument "org" { + yin-element false; + } + description + "This extension specifies the organization name that should be used within + the module catalogue on the device for the specified YANG module. It stores + a pithy string where the YANG organization statement may contain more + details."; + } + + extension origin { + argument "origin" { + yin-element false; + } + description + "This extension specifies the name of the origin that the YANG module + falls within. This allows multiple overlapping schema trees to be used + on a single network element without requiring module based prefixing + of paths."; + } +} diff --git a/models/yang/common/openconfig-if-aggregate.yang b/models/yang/common/openconfig-if-aggregate.yang new file mode 100644 index 0000000000..a8f18f7f82 --- /dev/null +++ b/models/yang/common/openconfig-if-aggregate.yang @@ -0,0 +1,232 @@ +module openconfig-if-aggregate { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces/aggregate"; + + prefix "oc-lag"; + + // import some basic types + import openconfig-interfaces { prefix oc-if; } + import openconfig-if-ethernet { prefix oc-eth; } + import iana-if-type { prefix ift; } + import openconfig-if-types { prefix oc-ift; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Model for managing aggregated (aka bundle, LAG) interfaces."; + + oc-ext:openconfig-version "2.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "2.3.2"; + } + + revision "2018-03-23" { + description + "Fix/cleanup when statements in aggregates model."; + reference "2.3.1"; + } + + revision "2018-01-05" { + description + "Add logical loopback to interface."; + reference "2.3.0"; + } + + revision "2017-12-22" { + description + "Add IPv4 proxy ARP configuration."; + reference "2.2.0"; + } + + revision "2017-12-21" { + description + "Added IPv6 router advertisement configuration."; + reference "2.1.0"; + } + + revision "2017-07-14" { + description + "Added Ethernet/IP state data; Add dhcp-client; + migrate to OpenConfig types modules; Removed or + renamed opstate values"; + reference "2.0.0"; + } + + revision "2016-12-22" { + description + "Fixes to Ethernet interfaces model"; + reference "1.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // extension statements + + // feature statements + + // identity statements + + // typedef statements + + typedef aggregation-type { + type enumeration { + enum LACP { + description "LAG managed by LACP"; + } + enum STATIC { + description "Statically configured bundle / LAG"; + } + } + description + "Type to define the lag-type, i.e., how the LAG is + defined and managed"; + } + + // grouping statements + + + grouping aggregation-logical-config { + description + "Configuration data for aggregate interfaces"; + + + leaf lag-type { + type aggregation-type; + description + "Sets the type of LAG, i.e., how it is + configured / maintained"; + } + + leaf min-links { + type uint16; + description + "Specifies the mininum number of member + interfaces that must be active for the aggregate interface + to be available"; + } + } + + grouping aggregation-logical-state { + description + "Operational state data for aggregate interfaces"; + + leaf lag-speed { + type uint32; + units Mbps; + description + "Reports effective speed of the aggregate interface, + based on speed of active member interfaces"; + } + + leaf-list member { + when "../../config/lag-type = 'STATIC'" { + description + "The simple list of member interfaces is active + when the aggregate is statically configured"; + } + type oc-if:base-interface-ref; + description + "List of current member interfaces for the aggregate, + expressed as references to existing interfaces"; + } + } + + grouping aggregation-logical-top { + description "Top-level data definitions for LAGs"; + + container aggregation { + + description + "Options for logical interfaces representing + aggregates"; + + container config { + description + "Configuration variables for logical aggregate / + LAG interfaces"; + + uses aggregation-logical-config; + } + + container state { + + config false; + description + "Operational state variables for logical + aggregate / LAG interfaces"; + + uses aggregation-logical-config; + uses aggregation-logical-state; + + } + } + } + + grouping ethernet-if-aggregation-config { + description + "Adds configuration items for Ethernet interfaces + belonging to a logical aggregate / LAG"; + + leaf aggregate-id { + type leafref { + path "/oc-if:interfaces/oc-if:interface/oc-if:name"; + } + description + "Specify the logical aggregate interface to which + this interface belongs"; + } + } + + // data definition statements + + // augment statements + + augment "/oc-if:interfaces/oc-if:interface" { + + description "Adds LAG configuration to the interface module"; + + uses aggregation-logical-top { + when "oc-if:state/oc-if:type = 'ift:ieee8023adLag' or " + + "oc-if:state/oc-if:type = 'oc-ift:IF_AGGREGATE'" { + description + "active when the interface is set to type LAG"; + } + } + } + + augment "/oc-if:interfaces/oc-if:interface/oc-eth:ethernet/" + + "oc-eth:config" { + description + "Adds LAG settings to individual Ethernet interfaces"; + + uses ethernet-if-aggregation-config; + } + + augment "/oc-if:interfaces/oc-if:interface/oc-eth:ethernet/" + + "oc-eth:state" { + description + "Adds LAG settings to individual Ethernet interfaces"; + + uses ethernet-if-aggregation-config; + } + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-if-ethernet-ext.yang b/models/yang/common/openconfig-if-ethernet-ext.yang new file mode 100644 index 0000000000..f64773b22d --- /dev/null +++ b/models/yang/common/openconfig-if-ethernet-ext.yang @@ -0,0 +1,117 @@ +module openconfig-if-ethernet-ext { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces/ethernet-ext"; + + prefix "oc-eth-ext"; + + // import some basic types + import openconfig-interfaces { prefix oc-if; } + import openconfig-if-ethernet { prefix oc-eth; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module adds extensions to the base ethernet configuration + and operational state model to support additional use cases."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2018-07-10" { + description + "Initial version of Ethernet extensions module to add frame + size distribution stats"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping ethernet-in-frames-size-dist { + description + "Grouping for defining the size distribution of the frames + received"; + + container in-distribution { + description + "The size distribution of the received frames."; + + leaf in-frames-64-octets { + type oc-yang:counter64; + description + "Number of packets (including bad packets) received that + were 64 bytes in length (excluding framing bits but + including FCS bytes)."; + } + + leaf in-frames-65-127-octets { + type oc-yang:counter64; + description + "Number of good and bad packets received that were + between 65 and 127 bytes in length (excluding framing bits + but including FCS bytes)."; + } + + leaf in-frames-128-255-octets { + type oc-yang:counter64; + description + "Number of good and bad packets received that were + between 128 and 255 bytes in length inclusive + (excluding framing bits but including FCS bytes)."; + } + + leaf in-frames-256-511-octets { + type oc-yang:counter64; + description + "Number of good and bad packets received that were + between 256 and 511 bytes in length inclusive + (excluding framing bits but including FCS bytes)."; + } + + leaf in-frames-512-1023-octets { + type oc-yang:counter64; + description + "Number of good and bad packets received that were + between 512 and 1023 bytes in length inclusive + (excluding framing bits but including FCS bytes)."; + } + + leaf in-frames-1024-1518-octets { + type oc-yang:counter64; + description + "Number of good and bad packets received that were + between 1024 and 1518 bytes in length inclusive + (excluding framing bits but including FCS bytes)."; + } + } + } + + // augment statements + + augment "/oc-if:interfaces/oc-if:interface/oc-eth:ethernet/" + + "oc-eth:state/oc-eth:counters" { + description + "Adds size distribution to the ethernet counters"; + + uses ethernet-in-frames-size-dist; + } + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-if-ip-ext.yang b/models/yang/common/openconfig-if-ip-ext.yang new file mode 100644 index 0000000000..2f28934971 --- /dev/null +++ b/models/yang/common/openconfig-if-ip-ext.yang @@ -0,0 +1,179 @@ +module openconfig-if-ip-ext { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces/ip-ext"; + + prefix "oc-ip-ext"; + + import openconfig-interfaces { prefix oc-if; } + import openconfig-if-ip { prefix oc-ip; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module adds extensions to the base IP configuration and + operational state model to support additional use cases."; + + oc-ext:openconfig-version "2.3.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "2.3.1"; + } + + revision "2018-01-05" { + description + "Add logical loopback to interface."; + reference "2.3.0"; + } + + revision "2017-12-21" { + description + "Added IPv6 router advertisement configuration."; + reference "2.1.0"; + } + + revision "2017-07-14" { + description + "Added Ethernet/IP state data; Add dhcp-client; + migrate to OpenConfig types modules; Removed or + renamed opstate values"; + reference "2.0.0"; + } + + revision "2016-12-22" { + description + "Fixes to Ethernet interfaces model"; + reference "1.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + grouping ipv6-autoconf-config { + description + "Configuration data for IPv6 address autoconfiguration"; + + leaf create-global-addresses { + type boolean; + default true; + description + "[adapted from IETF IP model RFC 7277] + + If enabled, the host creates global addresses as + described in RFC 4862."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration + Section 5.5"; + } + leaf create-temporary-addresses { + type boolean; + default false; + description + "[adapted from IETF IP model RFC 7277] + + If enabled, the host creates temporary addresses as + described in RFC 4941."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6"; + } + + leaf temporary-valid-lifetime { + type uint32; + units "seconds"; + default 604800; + description + "[adapted from IETF IP model RFC 7277] + + The time period during which the temporary address + is valid."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + - TEMP_VALID_LIFETIME"; + } + + leaf temporary-preferred-lifetime { + type uint32; + units "seconds"; + default 86400; + description + "[adapted from IETF IP model RFC 7277] + + The time period during which the temporary address is + preferred."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + - TEMP_PREFERRED_LIFETIME"; + } + } + + grouping ipv6-autoconf-state { + description + "Operational state data for IPv6 address autoconfiguration"; + + //TODO: placeholder for additional opstate for IPv6 autoconf + } + + grouping ipv6-autoconf-top { + description + "Top-level grouping for IPv6 address autoconfiguration"; + + container autoconf { + description + "Top-level container for IPv6 autoconf"; + + container config { + description + "[adapted from IETF IP model RFC 7277] + + Parameters to control the autoconfiguration of IPv6 + addresses, as described in RFC 4862."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + + uses ipv6-autoconf-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses ipv6-autoconf-config; + uses ipv6-autoconf-state; + } + } + } + + // data definition statements + + // augment statements + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface/oc-ip:ipv6" { + description + "Adds address autoconfiguration to the base IP model"; + + uses ipv6-autoconf-top; + } + +} diff --git a/models/yang/common/openconfig-if-poe.yang b/models/yang/common/openconfig-if-poe.yang new file mode 100644 index 0000000000..7758ea3186 --- /dev/null +++ b/models/yang/common/openconfig-if-poe.yang @@ -0,0 +1,110 @@ +module openconfig-if-poe { + + yang-version "1"; + + namespace "http://openconfig.net/yang/poe"; + + prefix "oc-poe"; + + import openconfig-if-ethernet { prefix oc-eth; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-extensions { prefix oc-ext; } + + organization "OpenConfig working group"; + + contact + "Openconfig working group + www.openconfig.net"; + + description + "This module defines configuration and state data for + Power over Ethernet (PoE) based on the IEEE 802.3af + standard."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2017-09-14" { + description + "Initial public revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping poe-ethernet-config { + description + "PoE ethernet config grouping"; + + leaf enabled { + type boolean; + default "true"; + description + "Enable or disable PoE in the ethernet interface."; + } + } + + grouping poe-ethernet-state { + description + "PoE ethernet state grouping"; + + leaf power-used { + type decimal64 { + fraction-digits 2; + } + units Watts; + description + "Power used by the ethernet interface in Watts."; + } + + leaf power-class { + type uint8; + description + "IEEE 802.3af Power class detected for this ethernet + interface."; + } + } + + grouping poe-ethernet-top { + description + "Ethernet top level grouping"; + + container poe { + description + "Top-level container for PoE configuration and state data"; + + container config { + description + "Configuration data for PoE"; + + uses poe-ethernet-config; + } + + container state { + config false; + + description + "Operational state data for PoE"; + + uses poe-ethernet-config; + uses poe-ethernet-state; + } + } + } + + augment "/oc-if:interfaces/oc-if:interface/oc-eth:ethernet" { + description + "Adds PoE to the ethernet model."; + + uses poe-ethernet-top; + } + +} diff --git a/models/yang/common/openconfig-if-tunnel.yang b/models/yang/common/openconfig-if-tunnel.yang new file mode 100644 index 0000000000..3003699d52 --- /dev/null +++ b/models/yang/common/openconfig-if-tunnel.yang @@ -0,0 +1,120 @@ +module openconfig-if-tunnel { + yang-version "1"; + + namespace "http://openconfig.net/yang/interfaces/tunnel"; + + prefix "oc-tun"; + + import openconfig-interfaces { prefix oc-if; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-inet-types { prefix oc-inet; } + import openconfig-if-ip { prefix oc-ip; } + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This model adds extensions to the OpenConfig interfaces + model to configure tunnel interfaces on a network + device."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2018-01-05" { + description + "Initial tunnel model"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping tunnel-top { + description + "Top-level grouping for parameters related to + a tunnel interface."; + + container tunnel { + description + "In the case that the interface is logical tunnel + interface, the parameters for the tunnel are + specified within this subtree. Tunnel interfaces + have only a single logical subinterface associated + with them."; + + container config { + description + "Configuration parameters associated with the + tunnel interface"; + uses tunnel-config; + } + + container state { + config false; + description + "Operational state parameters associated with + the tunnel interface."; + uses tunnel-config; + } + + uses oc-ip:ipv4-top; + uses oc-ip:ipv6-top; + } + } + + grouping tunnel-config { + description + "Configuraton parameters relating to a tunnel + interface."; + + leaf src { + type oc-inet:ip-address; + description + "The source address that should be used for the + tunnel."; + } + + leaf dst { + type oc-inet:ip-address; + description + "The destination address for the tunnel."; + } + + leaf ttl { + type uint8 { + range "1..255"; + } + description + "The time-to-live (or hop limit) that should be utilised + for the IP packets used for the tunnel transport."; + } + + leaf gre-key { + type uint32; + description + "The GRE key to be specified for the tunnel. The + key is used to identify a traffic flow within + a tunnel."; + reference + "RFC2890: Key and Sequence Number Extensions to GRE"; + } + } + + augment "/oc-if:interfaces/oc-if:interface" { + description + "Augment to add tunnel configuration to interfaces"; + uses tunnel-top; + } +} diff --git a/models/yang/common/openconfig-if-types.yang b/models/yang/common/openconfig-if-types.yang new file mode 100644 index 0000000000..27d2dc1d89 --- /dev/null +++ b/models/yang/common/openconfig-if-types.yang @@ -0,0 +1,108 @@ +module openconfig-if-types { + yang-version "1"; + + namespace "http://openconfig.net/yang/openconfig-if-types"; + + prefix "oc-ift"; + + // import statements + import openconfig-extensions { prefix oc-ext; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module contains a set of interface type definitions that + are used across OpenConfig models. These are generally physical + or logical interfaces, distinct from hardware ports (which are + described by the OpenConfig platform model)."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2018-01-05" { + description + "Add tunnel types into the INTERFACE_TYPE identity."; + reference "0.2.0"; + } + + revision "2016-11-14" { + description + "Initial version"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + identity INTERFACE_TYPE { + description + "Base identity from which interface types are derived."; + } + + identity IF_ETHERNET { + base INTERFACE_TYPE; + description + "Ethernet interfaces based on IEEE 802.3 standards, as well + as FlexEthernet"; + reference + "IEEE 802.3-2015 - IEEE Standard for Ethernet + OIF Flex Ethernet Implementation Agreement 1.0"; + } + + identity IF_AGGREGATE { + base INTERFACE_TYPE; + description + "An aggregated, or bonded, interface forming a + Link Aggregation Group (LAG), or bundle, most often based on + the IEEE 802.1AX (or 802.3ad) standard."; + reference + "IEEE 802.1AX-2008"; + } + + identity IF_LOOPBACK { + base INTERFACE_TYPE; + description + "A virtual interface designated as a loopback used for + various management and operations tasks."; + } + + identity IF_ROUTED_VLAN { + base INTERFACE_TYPE; + description + "A logical interface used for routing services on a VLAN. + Such interfaces are also known as switch virtual interfaces + (SVI) or integrated routing and bridging interfaces (IRBs)."; + } + + identity IF_SONET { + base INTERFACE_TYPE; + description + "SONET/SDH interface"; + } + + identity IF_TUNNEL_GRE4 { + base INTERFACE_TYPE; + description + "A GRE tunnel over IPv4 transport."; + } + + identity IF_TUNNEL_GRE6 { + base INTERFACE_TYPE; + description + "A GRE tunnel over IPv6 transport."; + } + +} diff --git a/models/yang/common/openconfig-igmp-types.yang b/models/yang/common/openconfig-igmp-types.yang new file mode 100644 index 0000000000..6e54f66eab --- /dev/null +++ b/models/yang/common/openconfig-igmp-types.yang @@ -0,0 +1,64 @@ +module openconfig-igmp-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/igmp/types"; + + prefix "oc-igmp-types"; + + // import some basic types + import openconfig-extensions { prefix "oc-ext"; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines types related to the IGMP protocol model."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2018-02-19" { + description + "Initial revision."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedef statements + + typedef igmp-version { + type uint8 { + range 1..3; + } + description + "IGMP Version."; + reference "v1 = RFC1112, v2 = RFC2236, v3 = RFC3376"; + } + + typedef igmp-interval-type { + type uint16 { + range 1..1024; + } + units "seconds"; + description + "Interval at which the router sends the IGMP query message toward + the upstream neighbor."; + reference "RFC3376 8.2 Page 40"; + } +} diff --git a/models/yang/common/openconfig-igmp.yang b/models/yang/common/openconfig-igmp.yang new file mode 100644 index 0000000000..a93a6d55c2 --- /dev/null +++ b/models/yang/common/openconfig-igmp.yang @@ -0,0 +1,367 @@ +module openconfig-igmp { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/igmp"; + + prefix "oc-igmp"; + + // import some basic types/interfaces + import openconfig-igmp-types { prefix oc-igmp-types; } + import openconfig-types { prefix "oc-types"; } + import openconfig-acl { prefix "oc-acl"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-interfaces { prefix oc-if; } + import ietf-inet-types { prefix "inet"; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "An OpenConfig model for Internet Group Management Protocol (IGMP)."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2018-02-19" { + description + "Initial revision."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping admin-config { + description + "Re-usable grouping to enable or disable a particular feature."; + + leaf enabled { + type boolean; + default false; + description + "When set to true, the functionality within which this + leaf is defined is enabled, when set to false it is + explicitly disabled."; + } + } + + grouping igmp-interface-config { + description + "Configuration data for IGMP on each interface."; + + leaf interface-id { + type oc-if:interface-id; + description + "Reference to an interface on which IGMP is enabled."; + } + + uses admin-config; + + leaf version { + type oc-igmp-types:igmp-version; + description + "IGMP Version."; + } + + leaf query-interval { + type oc-igmp-types:igmp-interval-type; + description + "Interval at which the router sends the IGMP membership + queries."; + } + + leaf filter-prefixes { + type string; + // TODO work out what this should be. + // On Juniper it's a "policy" and on Cisco a sort of "class map" + description + "List used to filter joins."; + } + } + + grouping igmp-counters-per-version { + description + "Counters for each IGMP protocol version."; + + container state { + config false; + description + "Counters for each IGMP protocol version."; + + leaf v1 { + type uint32; + description + "IGMP v1."; + } + leaf v2 { + type uint32; + description + "IGMP v2."; + } + leaf v3 { + type uint32; + description + "IGMP v3."; + } + } + } + + grouping igmp-interface-counters { + description + "State and session data for IGMP on each interface."; + + + container counters { + description + "Counters avaiable on a per interface bases for IGMP."; + + container queries { + description + "IGMP membership queries."; + + container sent { + description + "Number of IGMP membership queries sent."; + uses igmp-counters-per-version; + } + + container received { + description + "Number of IGMP membership queries received."; + uses igmp-counters-per-version; + } + } + + container reports { + description + "Number of IGMP membership reports received."; + uses igmp-counters-per-version; + } + } + } + + grouping igmp-snooping-state { + description + "IGMP membership snooping state."; + + leaf group { + type inet:ipv4-address; + description + "Multicast address."; + } + + leaf source { + type inet:ipv4-address; + description + "Source address of multicast."; + } + + leaf reporter { + type inet:ipv4-address; + description + "Address of the last reporter."; + } + } + + grouping igmp-snooping-structural { + description + "IGMP membership information determined through snooping."; + + container membership-groups { + description + "List of IGMP Membership information."; + + list group { + key "group"; + config false; + description + "Multicast group membership."; + + leaf group { + type leafref { + path "../state/group"; + } + description + "Multicast address."; + } + + container state { + config false; + description + "Multicast group membership."; + + uses igmp-snooping-state; + } + } + } + } + + grouping igmp-interface-state { + description + "IGMP interface state."; + + leaf query-expires { + type oc-types:timeticks64; + description + "This timestamp indicates the time that the next query is sent. + The value is the timestamp in seconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + } + + grouping igmp-interface-top { + description + "Configuration and state data for IGMP on each interface."; + + container interfaces { + description + "The interfaces on which IGMP is configured."; + + list interface { + key "interface-id"; + description + "This container defines interface IGMP configuration and + state information."; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "Reference to an interface on which IGMP is enabled."; + } + + container config { + description + "IGMP interface configuration."; + + uses igmp-interface-config; + } + + container state { + config false; + description + "This container defines state information for IGMP + interfaces."; + + uses igmp-interface-state; + uses igmp-interface-config; + } + + uses igmp-interface-counters; + uses igmp-snooping-structural; + uses oc-if:interface-ref; + } + } + } + + grouping igmp-ssm-maps-config { + description + "A Source Specific Multicast (SSM) mapping. This allows + IGMP v2 hosts to be able to join in SSM environments + by translating IGMP v2 reports into IGMP v3 reports. + The request in an IGMP v2 join is sent toward the source + address found by matching the multicast address."; + + leaf source { + type inet:ipv4-address; + description + "Multicast source address."; + } + + leaf ssm-ranges { + type leafref { + path "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/" + + "oc-acl:config/oc-acl:name"; + } + description + "List of accepted source specific multicast (SSM) address + ranges."; + } + } + + grouping igmp-global-config { + description + "This grouping defines global config options for IGMP."; + + } + + grouping igmp-global-top { + description + "Top level grouping for global IGMP configuration."; + + container ssm { + description + "Source specific multicast (SSM)."; + + container mappings { + description + "A list of source specific multicast (SSM) mappings."; + + list mapping { + key "source"; + description + "A Source Specific Multicast (SSM) mapping. This allows + IGMP v2 hosts to be able to join in SSM environments + by translating IGMP v2 reports into IGMP v3 reports. + The request in an IGMP v2 join is sent toward the source + address found by matching the multicast address."; + + leaf source { + type leafref { + path "../config/source"; + } + description + "Multicast source address."; + } + + container config { + description + "Configuration for SSM maps."; + uses igmp-ssm-maps-config; + } + container state { + config false; + description + "State for SSM maps."; + uses igmp-ssm-maps-config; + } + } + } + } + } + + grouping igmp-top { + description + "Top-level grouping for IGMP."; + + container igmp { + description + "Top-level IGMP configuration and operational state."; + + container global { + description + "Global IGMP configuration and operational state."; + uses igmp-global-top; + } + + uses igmp-interface-top; + } + } + + // data definition statements +} diff --git a/models/yang/common/openconfig-inet-types.yang b/models/yang/common/openconfig-inet-types.yang new file mode 100644 index 0000000000..7c23d2b38b --- /dev/null +++ b/models/yang/common/openconfig-inet-types.yang @@ -0,0 +1,343 @@ +module openconfig-inet-types { + + yang-version "1"; + namespace "http://openconfig.net/yang/types/inet"; + prefix "oc-inet"; + + import openconfig-extensions { prefix "oc-ext"; } + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module contains a set of Internet address related + types for use in OpenConfig modules. + + Portions of this code were derived from IETF RFC 6021. + Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "0.3.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.2"; + } + + revision 2017-08-24 { + description + "Minor formatting fixes."; + reference "0.3.1"; + } + + revision 2017-07-06 { + description + "Add domain-name and host typedefs"; + reference "0.3.0"; + } + + revision 2017-04-03 { + description + "Add ip-version typedef."; + reference "0.2.0"; + } + + revision 2017-04-03 { + description + "Update copyright notice."; + reference "0.1.1"; + } + + revision 2017-01-26 { + description + "Initial module for inet types"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // IPv4 and IPv6 types. + + 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])$'; + } + description + "An IPv4 address in dotted quad notation using the default + zone."; + } + + typedef ipv4-address-zoned { + 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])(%[a-zA-Z0-9_]+)$'; + } + description + "An IPv4 address in dotted quad notation. This type allows + specification of a zone index to disambiguate identical + address values. For link-local addresses, the index is + typically the interface index or interface name."; + } + + typedef ipv6-address { + type string { + pattern + // Must support compression through different lengths + // therefore this regexp is complex. + '^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|' + + '([0-9a-fA-F]{1,4}:){1,7}:|' + + '([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|' + + '([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|' + + '([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|' + + '([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|' + + '([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|' + + '[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|' + + ':((:[0-9a-fA-F]{1,4}){1,7}|:)' + + ')$'; + } + description + "An IPv6 address represented as either a full address; shortened + or mixed-shortened formats, using the default zone."; + } + + typedef ipv6-address-zoned { + type string { + pattern + // Must support compression through different lengths + // therefore this regexp is complex. + '^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|' + + '([0-9a-fA-F]{1,4}:){1,7}:|' + + '([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|' + + '([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|' + + '([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|' + + '([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|' + + '([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|' + + '[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|' + + ':((:[0-9a-fA-F]{1,4}){1,7}|:)' + + ')(%[a-zA-Z0-9_]+)$'; + } + description + "An IPv6 address represented as either a full address; shortened + or mixed-shortened formats. This type allows specification of + a zone index to disambiguate identical address values. For + link-local addresses, the index is typically the interface + index or interface name."; + } + + 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 + "An IPv4 prefix represented in dotted quad notation followed by + a slash and a CIDR mask (0 <= mask <= 32)."; + } + + typedef ipv6-prefix { + type string { + pattern + '^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|' + + '([0-9a-fA-F]{1,4}:){1,7}:|' + + '([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}' + + '([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|' + + '([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|' + + '([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|' + + '([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|' + + '[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|' + + ':((:[0-9a-fA-F]{1,4}){1,7}|:)' + + ')/(12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9])$'; + } + description + "An IPv6 prefix represented in full, shortened, or mixed + shortened format followed by a slash and CIDR mask + (0 <= mask <= 128)."; + } + + typedef ip-address { + type union { + type ipv4-address; + type ipv6-address; + } + description + "An IPv4 or IPv6 address with no prefix specified."; + } + + typedef ip-prefix { + type union { + type ipv4-prefix; + type ipv6-prefix; + } + description + "An IPv4 or IPv6 prefix."; + } + + typedef ip-version { + type enumeration { + enum UNKNOWN { + value 0; + description + "An unknown or unspecified version of the Internet + protocol."; + } + enum IPV4 { + value 4; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum IPV6 { + value 6; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + Note that integer representation of the enumerated values + are not specified, and are not required to follow the + InetVersion textual convention in SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + 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. + Fully quallified left to the models which utilize this type. + + 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. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be encoded in punycode as described in RFC + 3492"; + } + + typedef host { + type union { + type ip-address; + type domain-name; + } + description + "The host type represents either an unzoned IP address or a DNS + domain name."; + } + + typedef as-number { + type uint32; + description + "A numeric identifier for an autonomous system (AS). An AS is a + single domain, under common administrative control, which forms + a unit of routing policy. Autonomous systems can be assigned a + 2-byte identifier, or a 4-byte identifier which may have public + or private scope. Private ASNs are assigned from dedicated + ranges. Public ASNs are assigned from ranges allocated by IANA + to the regional internet registries (RIRs)."; + reference + "RFC 1930 Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271 A Border Gateway Protocol 4 (BGP-4)"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "A differentiated services code point (DSCP) marking within the + IP header."; + reference + "RFC 2474 Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The IPv6 flow-label is a 20-bit value within the IPv6 header + which is optionally used by the source of the IPv6 packet to + label sets of packets for which special handling may be + required."; + reference + "RFC 2460 Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16; + description + "A 16-bit port number used by a transport protocol such as TCP + or UDP."; + reference + "RFC 768 User Datagram Protocol + RFC 793 Transmission Control Protocol"; + } + + typedef uri { + type string; + description + "An ASCII-encoded Uniform Resource Identifier (URI) as defined + in RFC 3986."; + reference + "RFC 3986 Uniform Resource Identifier (URI): Generic Syntax"; + } + + typedef url { + type string; + description + "An ASCII-encoded Uniform Resource Locator (URL) as defined + in RFC 3986, section 1.1.3"; + reference + "RFC 3986, paragraph 1.1.3"; + } + +} diff --git a/models/yang/common/openconfig-interfaces.yang b/models/yang/common/openconfig-interfaces.yang new file mode 100644 index 0000000000..f3e0feeace --- /dev/null +++ b/models/yang/common/openconfig-interfaces.yang @@ -0,0 +1,1067 @@ +module openconfig-interfaces { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces"; + + prefix "oc-if"; + + // import some basic types + import ietf-interfaces { prefix ietf-if; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-types { prefix oc-types; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Model for managing network interfaces and subinterfaces. This + module also defines convenience types / groupings for other + models to create references to interfaces: + + base-interface-ref (type) - reference to a base interface + interface-ref (grouping) - container for reference to a + interface + subinterface + interface-ref-state (grouping) - container for read-only + (opstate) reference to interface + subinterface + + This model reuses data items defined in the IETF YANG model for + interfaces described by RFC 7223 with an alternate structure + (particularly for operational state data) and with + additional configuration items. + + Portions of this code were derived from IETF RFC 7223. + Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "2.4.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "2.4.1"; + } + + revision "2018-08-07" { + description + "Add leaf to indicate whether an interface is physical or + logical."; + reference "2.4.0"; + } + + revision "2018-07-02" { + description + "Add in-pkts and out-pkts in counters"; + reference "2.3.2"; + } + + revision "2018-04-24" { + description + "Clarified behavior of last-change state leaf"; + reference "2.3.1"; + } + + revision "2018-01-05" { + description + "Add logical loopback to interface."; + reference "2.3.0"; + } + + revision "2017-12-22" { + description + "Add IPv4 proxy ARP configuration."; + reference "2.2.0"; + } + + revision "2017-12-21" { + description + "Added IPv6 router advertisement configuration."; + reference "2.1.0"; + } + + revision "2017-07-14" { + description + "Added Ethernet/IP state data; Add dhcp-client; + migrate to OpenConfig types modules; Removed or + renamed opstate values"; + reference "2.0.0"; + } + + revision "2017-04-03" { + description + "Update copyright notice."; + reference "1.1.1"; + } + + revision "2016-12-22" { + description + "Fixes to Ethernet interfaces model"; + reference "1.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedef statements + + typedef base-interface-ref { + type leafref { + path "/oc-if:interfaces/oc-if:interface/oc-if:name"; + } + description + "Reusable type for by-name reference to a base interface. + This type may be used in cases where ability to reference + a subinterface is not required."; + } + + typedef interface-id { + type string; + description + "User-defined identifier for an interface, generally used to + name a interface reference. The id can be arbitrary but a + useful convention is to use a combination of base interface + name and subinterface index."; + } + + // grouping statements + + grouping interface-ref-common { + description + "Reference leafrefs to interface / subinterface"; + + leaf interface { + type leafref { + path "/oc-if:interfaces/oc-if:interface/oc-if:name"; + } + description + "Reference to a base interface. If a reference to a + subinterface is required, this leaf must be specified + to indicate the base interface."; + } + + leaf subinterface { + type leafref { + path "/oc-if:interfaces/" + + "oc-if:interface[oc-if:name=current()/../interface]/" + + "oc-if:subinterfaces/oc-if:subinterface/oc-if:index"; + } + description + "Reference to a subinterface -- this requires the base + interface to be specified using the interface leaf in + this container. If only a reference to a base interface + is requuired, this leaf should not be set."; + } + } + + grouping interface-ref-state-container { + description + "Reusable opstate w/container for a reference to an + interface or subinterface"; + + container state { + config false; + description + "Operational state for interface-ref"; + + uses interface-ref-common; + } + } + + grouping interface-ref { + description + "Reusable definition for a reference to an interface or + subinterface"; + + container interface-ref { + description + "Reference to an interface or subinterface"; + + container config { + description + "Configured reference to interface / subinterface"; + oc-ext:telemetry-on-change; + + uses interface-ref-common; + } + + uses interface-ref-state-container; + } + } + + grouping interface-ref-state { + description + "Reusable opstate w/container for a reference to an + interface or subinterface"; + + container interface-ref { + description + "Reference to an interface or subinterface"; + + uses interface-ref-state-container; + } + } + + grouping base-interface-ref-state { + description + "Reusable opstate w/container for a reference to a + base interface (no subinterface)."; + + container state { + config false; + description + "Operational state for base interface reference"; + + leaf interface { + type base-interface-ref; + description + "Reference to a base interface."; + } + } + } + + + grouping interface-common-config { + description + "Configuration data data nodes common to physical interfaces + and subinterfaces"; + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation to + + decide whether to modify this single leaf in 'startup' or + perform an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + } + + grouping interface-phys-config { + description + "Configuration data for physical interfaces"; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list interfaces/interface[name]/state contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + corresponding state list, the server MAY reject + the request if the implementation does not support + pre-provisioning of interfaces or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + The IETF model in RFC 7223 provides YANG features for the + following (i.e., pre-provisioning and arbitrary-names), + however they are omitted here: + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interfaces/interface[name]/state list."; + } + + leaf type { + type identityref { + base ietf-if:interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf mtu { + type uint16; + description + "Set the max transmission unit size in octets + for the physical interface. If this is not set, the mtu is + set to the operational default -- e.g., 1514 bytes on an + Ethernet interface."; + } + + leaf loopback-mode { + type boolean; + default false; + description + "When set to true, the interface is logically looped back, + such that packets that are forwarded via the interface + are received on the same interface."; + } + + uses interface-common-config; + } + + grouping interface-phys-holdtime-config { + description + "Configuration data for interface hold-time settings -- + applies to physical interfaces."; + + leaf up { + type uint32; + units milliseconds; + default 0; + description + "Dampens advertisement when the interface + transitions from down to up. A zero value means dampening + is turned off, i.e., immediate notification."; + } + + leaf down { + type uint32; + units milliseconds; + default 0; + description + "Dampens advertisement when the interface transitions from + up to down. A zero value means dampening is turned off, + i.e., immediate notification."; + } + } + + grouping interface-phys-holdtime-state { + description + "Operational state data for interface hold-time."; + } + + grouping interface-phys-holdtime-top { + description + "Top-level grouping for setting link transition + dampening on physical and other types of interfaces."; + + container hold-time { + description + "Top-level container for hold-time settings to enable + dampening advertisements of interface transitions."; + + container config { + description + "Configuration data for interface hold-time settings."; + oc-ext:telemetry-on-change; + + uses interface-phys-holdtime-config; + } + + container state { + + config false; + + description + "Operational state data for interface hold-time."; + + uses interface-phys-holdtime-config; + uses interface-phys-holdtime-state; + } + } + } + + grouping interface-common-state { + description + "Operational state data (in addition to intended configuration) + at the global level for this interface"; + + oc-ext:operational; + + leaf ifindex { + type uint32; + description + "System assigned number for each interface. Corresponds to + ifIndex object in SNMP Interface MIB"; + reference + "RFC 2863 - The Interfaces Group MIB"; + oc-ext:telemetry-on-change; + } + + leaf admin-status { + type enumeration { + enum UP { + description + "Ready to pass packets."; + } + enum DOWN { + description + "Not ready to pass packets and not in some test mode."; + } + enum TESTING { + //TODO: This is generally not supported as a configured + //admin state, though it's in the standard interfaces MIB. + //Consider removing it. + description + "In some test mode."; + } + } + //TODO:consider converting to an identity to have the + //flexibility to remove some values defined by RFC 7223 that + //are not used or not implemented consistently. + mandatory true; + description + "The desired state of the interface. In RFC 7223 this leaf + has the same read semantics as ifAdminStatus. Here, it + reflects the administrative state as set by enabling or + disabling the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + oc-ext:telemetry-on-change; + } + + leaf oper-status { + type enumeration { + enum UP { + value 1; + description + "Ready to pass packets."; + } + enum DOWN { + value 2; + description + "The interface does not pass any packets."; + } + enum TESTING { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum UNKNOWN { + value 4; + description + "Status cannot be determined for some reason."; + } + enum DORMANT { + value 5; + description + "Waiting for some external event."; + } + enum NOT_PRESENT { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum LOWER_LAYER_DOWN { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + //TODO:consider converting to an identity to have the + //flexibility to remove some values defined by RFC 7223 that + //are not used or not implemented consistently. + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + oc-ext:telemetry-on-change; + } + + leaf last-change { + type oc-types:timeticks64; + units nanoseconds; + description + "This timestamp indicates the absolute time of the last + state change of the interface (e.g., up-to-down transition). + This is different than the SNMP ifLastChange object in the + standard interface MIB in that it is not relative to the + system boot time (i.e,. sysUpTime). + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + oc-ext:telemetry-on-change; + } + + leaf logical { + type boolean; + description + "When set to true, the interface is a logical interface + which does not have an associated physical port or + channel on the system."; + oc-ext:telemetry-on-change; + } + } + + + grouping interface-counters-state { + description + "Operational state representing interface counters + and statistics."; + + //TODO: we may need to break this list of counters into those + //that would appear for physical vs. subinterface or logical + //interfaces. For now, just replicating the full stats + //grouping to both interface and subinterface. + + oc-ext:operational; + + container counters { + description + "A collection of interface-related statistics objects."; + + leaf in-octets { + type oc-yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-pkts { + type oc-yang:counter64; + description + "The total number of packets received on the interface, + including all unicast, multicast, broadcast and bad packets + etc."; + reference + "RFC 2819: Remote Network Monitoring Management Information + Base"; + } + + leaf in-unicast-pkts { + type oc-yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type oc-yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type oc-yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type oc-yang:counter64; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + + + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type oc-yang:counter64; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type oc-yang:counter64; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf in-fcs-errors { + type oc-yang:counter64; + description + "Number of received packets which had errors in the + frame check sequence (FCS), i.e., framing errors. + + Discontinuities in the value of this counter can occur + when the device is re-initialization as indicated by the + value of 'last-clear'."; + } + + leaf out-octets { + type oc-yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-pkts { + type oc-yang:counter64; + description + "The total number of packets transmitted out of the + interface, including all unicast, multicast, broadcast, + and bad packets etc."; + reference + "RFC 2819: Remote Network Monitoring Management Information + Base"; + } + + leaf out-unicast-pkts { + type oc-yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type oc-yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + + leaf out-multicast-pkts { + type oc-yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type oc-yang:counter64; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type oc-yang:counter64; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + + leaf carrier-transitions { + type oc-yang:counter64; + description + "Number of times the interface state has transitioned + between up and down since the time the device restarted + or the last-clear time, whichever is most recent."; + oc-ext:telemetry-on-change; + } + + leaf last-clear { + type oc-types:timeticks64; + units nanoseconds; + description + "Timestamp of the last time the interface counters were + cleared. + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + oc-ext:telemetry-on-change; + } + } + } + + // data definition statements + + grouping sub-unnumbered-config { + description + "Configuration data for unnumbered subinterfaces"; + + leaf enabled { + type boolean; + default false; + description + "Indicates that the subinterface is unnumbered. By default + the subinterface is numbered, i.e., expected to have an + IP address configuration."; + } + } + + grouping sub-unnumbered-state { + description + "Operational state data unnumbered subinterfaces"; + } + + grouping sub-unnumbered-top { + description + "Top-level grouping unnumbered subinterfaces"; + + container unnumbered { + description + "Top-level container for setting unnumbered interfaces. + Includes reference the interface that provides the + address information"; + + container config { + description + "Configuration data for unnumbered interface"; + oc-ext:telemetry-on-change; + + uses sub-unnumbered-config; + } + + container state { + + config false; + + description + "Operational state data for unnumbered interfaces"; + + uses sub-unnumbered-config; + uses sub-unnumbered-state; + } + + uses oc-if:interface-ref; + } + } + + grouping subinterfaces-config { + description + "Configuration data for subinterfaces"; + + leaf index { + type uint32; + default 0; + description + "The index of the subinterface, or logical interface number. + On systems with no support for subinterfaces, or not using + subinterfaces, this value should default to 0, i.e., the + default subinterface."; + } + + uses interface-common-config; + + } + + grouping subinterfaces-state { + description + "Operational state data for subinterfaces"; + + oc-ext:operational; + + leaf name { + type string; + description + "The system-assigned name for the sub-interface. This MAY + be a combination of the base interface name and the + subinterface index, or some other convention used by the + system."; + oc-ext:telemetry-on-change; + } + + uses interface-common-state; + uses interface-counters-state; + } + + grouping subinterfaces-top { + description + "Subinterface data for logical interfaces associated with a + given interface"; + + container subinterfaces { + description + "Enclosing container for the list of subinterfaces associated + with a physical interface"; + + list subinterface { + key "index"; + + description + "The list of subinterfaces (logical interfaces) associated + with a physical interface"; + + leaf index { + type leafref { + path "../config/index"; + } + description + "The index number of the subinterface -- used to address + the logical interface"; + } + + container config { + description + "Configurable items at the subinterface level"; + oc-ext:telemetry-on-change; + + uses subinterfaces-config; + } + + container state { + + config false; + description + "Operational state data for logical interfaces"; + + uses subinterfaces-config; + uses subinterfaces-state; + } + } + } + } + + grouping interfaces-top { + description + "Top-level grouping for interface configuration and + operational state data"; + + container interfaces { + description + "Top level container for interfaces, including configuration + and state data."; + + + list interface { + key "name"; + + description + "The list of named interfaces on the device."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "References the configured name of the interface"; + //TODO: need to consider whether this should actually + //reference the name in the state subtree, which + //presumably would be the system-assigned name, or the + //configured name. Points to the config/name now + //because of YANG 1.0 limitation that the list + //key must have the same "config" as the list, and + //also can't point to a non-config node. + } + + container config { + description + "Configurable items at the global, physical interface + level"; + oc-ext:telemetry-on-change; + + uses interface-phys-config; + } + + container state { + + config false; + description + "Operational state data at the global interface level"; + + uses interface-phys-config; + uses interface-common-state; + uses interface-counters-state; + } + + uses interface-phys-holdtime-top; + uses subinterfaces-top; + } + } + } + + uses interfaces-top; + +} diff --git a/models/yang/common/openconfig-isis-lsdb-types.yang b/models/yang/common/openconfig-isis-lsdb-types.yang new file mode 100644 index 0000000000..db6e5f783d --- /dev/null +++ b/models/yang/common/openconfig-isis-lsdb-types.yang @@ -0,0 +1,703 @@ +module openconfig-isis-lsdb-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/isis-lsdb-types"; + + prefix "oc-isis-lsdb-types"; + + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module contains general LSDB type definitions for use in + ISIS YANG model. "; + + oc-ext:openconfig-version "0.4.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements."; + reference "0.4.1"; + } + + revision "2018-05-14" { + description + "Update LSDB model to correct Extended IS reach TLV + bug. This change is backwards incompatible due to + adding an additional level of hierarchy to support + multiple instances of the TLV."; + reference "0.4.0"; + } + + revision "2017-07-26" { + description + "Update LSDB and fix bugs."; + reference "0.3.2"; + } + + revision "2017-05-15" { + description + "Refactor LSDB."; + reference "0.3.0"; + } + + revision "2017-01-13" { + description + "Remove top-level /isis container"; + reference "0.2.1"; + } + + revision "2016-12-15" { + description + "Add segment routing to IS-IS module"; + reference "0.2.0"; + } + + revision "2016-10-18" { + description + "Initial revision of IS-IS models."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + identity ISIS_TLV_TYPE { + description + "Base identity for an ISIS TLV type."; + } + + identity ISIS_SUBTLV_TYPE { + description + "Base identity for an ISIS SUB-TLV type."; + } + + identity IS_REACHABILITY_SUBTLVS_TYPE { + base "ISIS_SUBTLV_TYPE"; + description + "Base identity for an ISIS TLV 22, 23, 222, 223, 141 SUB-TLV + type."; + } + + identity IP_REACHABILITY_SUBTLVS_TYPE { + base "ISIS_SUBTLV_TYPE"; + description + "Base identity for an ISIS TLV 135, 235, 236, 237 SUB-TLV + type."; + } + + identity ROUTER_CAPABILITY_SUBTLVS_TYPE { + base "ISIS_SUBTLV_TYPE"; + description + "Base identity for an ISIS TLV 242 SUB-TLV type."; + } + + identity AREA_ADDRESSES { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 1. Intermediate System to Intermediate System Intra- + Domain Routeing Exchange Protocol for use in Conjunction with + the Protocol for Providing the Connectionless-mode Network + Service (ISO 8473), International Standard 10589: 2002, Second + Edition, 2002."; + reference + "ISO 10589"; + } + + identity IIS_NEIGHBORS { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 2. Intermediate System to Intermediate System Intra- + Domain Routeing Exchange Protocol for use in Conjunction with + the Protocol for Providing the Connectionless-mode Network + Service (ISO 8473), International Standard 10589: 2002, Second + Edition, 2002."; + reference + "ISO 10589"; + } + + identity INSTANCE_ID { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 7. An Instance Identifier (IID) to uniquely + identify an IS-IS instance. When the IID = 0, the list of + supported ITIDs MUST NOT be present. An IID-TLV with IID = 0 + MUST NOT appear in an SNP or LSP. When the TLV appears (with a + non-zero IID) in an SNP or LSP, exactly one ITID. MUST be + present indicating the topology with which the PDU is + associated. If no ITIDs or multiple ITIDs are present or the + IID is zero, then the PDU MUST be ignored"; + reference + "RFC6822: IS-IS Multi-Instance"; + } + + identity AUTHENTICATION { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 10.Intermediate System to Intermediate System Intra- + Domain Routeing Exchange Protocol for use in Conjunction with + the Protocol for Providing the Connectionless-mode Network + Service (ISO 8473) International Standard 10589: 2002, Second + Edition, 2002."; + reference + "ISO 10589"; + } + + identity PURGE_OI { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 13. If an IS generates a purge, it SHOULD include + this TLV in the purge with its own system ID. If an IS + receives a purge that does not include this TLV, then it SHOULD + add this TLV with both its own system ID and the system ID of + the IS from which it received the purge. This allows ISs + receiving purges to log the system ID of the originator, or the + upstream source of the purge."; + reference + "RFC6232: Purge Originator Identification TLV"; + } + + identity LSP_BUFFER_SIZE { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 14. The maximum MTU that the advertising system can + receive, expressed in bytes."; + reference + "ISO 10589: LSP Buffer Size TLV"; + } + + identity EXTENDED_IS_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 22. An extended IS reachability TLV that has a + different data structure to TLV 2 that introduces the use of + sub-TLV object-group."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering"; + } + + identity IS_NEIGHBOR_ATTRIBUTE { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 23. Identical in format to TLV 22 and included in + Original LSPs or Extended LSPs. Regardless of the type of LSP + in which the TLVs appear, the information pertains to the + neighbor relationship between the Originating System and the IS + identified in the TLV"; + reference + "RFC5311: Simplified Extension of Link State PDU (LSP) Space + for IS-IS"; + } + + identity ISIS_ALIAS_ID { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 24. IS-Alias TLV which extension-capable ISs to + recognize the Originating System of an Extended LSP set. It + identifies the Normal system-id of the Originating System"; + reference + "RFC5311: Simplified Extension of Link State PDU (LSP) Space + for IS-IS"; + } + + identity IPV4_INTERNAL_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 128. TLV defines IP addresses within the routing + domain reachable directly via one or more interfaces on this + Intermediate system"; + reference + "RFC1195: OSI ISIS for IP and Dual Environments. RFC5302: + Domain-Wide Prefix Distribution with Two-Level IS-IS"; + } + + identity NLPID { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 129. TLV defines the set Network Layer Protocol + Identifiers for Network Layer protocols that this Intermediate + System is capable of relaying"; + reference + "RFC1195: Use of OSI IS-IS for Routing in TCP/IP and + Dual Environments"; + } + + identity IPV4_EXTERNAL_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 130. TLV defines IP addresses outside the routing + domain reachable via interfaces on this Intermediate system. + This is permitted to appear multiple times, and in an LSP with + any LSP number. However, this field must not appear in + pseudonode LSPs"; + reference " + RFC1195: OSI ISIS for IP and Dual Environments. RFC5302: + Domain-Wide Prefix Distribution with Two-Level IS-IS"; + } + + identity IPV4_INTERFACE_ADDRESSES { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 132. The IP address of one or more interfaces + corresponding to the SNPAs enabled on this Intermediate system + (i.e., one or more IP addresses of this router). This is + permitted to appear multiple times, and in an LSP with any LSP + number."; + reference + "RFC1195: Use of OSI IS-IS for Routing in TCP/IP and Dual + Environments"; + } + + identity IPV4_TE_ROUTER_ID { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 134. Traffic Engineering router ID TLV that contains + the 4-octet router ID of the router originating the LSP"; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering"; + } + + identity EXTENDED_IPV4_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 135. Extended IP reachability TLV that provides for a + 32-bit metric and adds one bit to indicate that a prefix has + been redistributed _down_ in the hierarchy"; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering"; + } + + identity DYNAMIC_NAME { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 137. The Dynamic hostname TLV is optional. This TLV + may be present in any fragment of a non-pseudonode LSP. The + value field identifies the symbolic name of the router + originating the LSP. This symbolic name can be the FQDN for the + router, it can be a subset of the FQDN, or it can be any string + operators want to use for the router."; + reference + "RFC6233: IS-IS Registry Extension for Purges, RFC 5301: Dynamic + Hostname Exchange Mechanism for IS-IS."; + } + + identity IPV4_SRLG { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 138. IPv4 Shared Risk Link Group TLV"; + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + } + + identity IPV6_SRLG { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 139. IPv6 Shared Risk Link Group"; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS"; + } + + identity IPV6_TE_ROUTER_ID { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 140. The IPv6 TE Router ID TLV contains a 16-octet + IPv6 address. A stable global IPv6 address MUST be used, so that + the router ID provides a routable address, regardless of the + state of a node's interfaces. If a router does not implement + traffic engineering, it MAY include or omit the IPv6 TE Router + ID TLV. If a router implements traffic engineering for IPv6, it + MUST include this TLV in its LSP. This TLV MUST NOT be included + more than once in an LSP."; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS."; + } + + identity MT_ISN { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 222. TLV is aligned with extended IS reachability TLV + type 22 beside an additional two bytes in front at the beginning + of the TLV that. indicate MT membership."; + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in Intermediate + System to Intermediate Systems (IS-ISs)"; + } + + identity MT_IS_NEIGHBOR_ATTRIBUTE { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 223. Is is identical in format to TLV 222. In the + event that there is a need to advertise in Extended LSPs such + information associated with neighbors of the Originating System, + it is necessary to define new TLVs to carry the sub-TLV + information."; + reference + "RFC5311: Simplified Extension of Link State PDU (LSP) Space for + IS-IS"; + } + + identity MULTI_TOPOLOGY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 229. This MT TLV can advertise up to 127 MTs. It is + announced in IIHs and LSP fragment 0, and can occur multiple + times. The resulting MT set SHOULD be the union of all the MT + TLV occurrences in the packet. Any other IS-IS PDU occurrence of + this TLV MUST be ignored. Lack of MT TLV in hellos and fragment + zero LSPs MUST be interpreted as participation of the + advertising interface or router in MT ID #0 only. If a router + advertises MT TLV, it has to advertise all the MTs it + participates in, specifically including topology ID #0 also."; + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in Intermediate + System to Intermediate Systems (IS-ISs)"; + } + + identity IPV6_INTERFACE_ADDRESSES { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 232. IPv6 Interface Address TLV that maps directly to + the IP Interface Address TLV in [RFC1195]. We necessarily modify + the contents to be 0-15 16-octet IPv6 interface addresses + instead of 0-63 4-octet IPv4 interface addresses"; + reference "RFC5308: Routing IPv6 with IS-IS."; + } + + identity MT_IPV4_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 235. TLV is aligned with extended IP reachability TLV + type 135 beside an additional two bytes in front to indicate MT + membership"; + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in Intermediate + System to Intermediate Systems (IS-ISs)"; + } + + identity IPV6_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 236. The IPv6 Reachability TLV describes network + reachability through the specification of a routing prefix, + metric information, a bit to indicate if the prefix is being + advertised down from a higher level, a bit to indicate if the + prefix is being distributed from another routing protocol, and + OPTIONALLY the existence of Sub-TLVs to allow for later + extension."; + reference + "RFC5308: Routing IPv6 with IS-IS"; + } + + identity MT_IPV6_REACHABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 237. TLV is aligned with IPv6 Reachability TLV type + 236 beside an additional two bytes in front to indicate MT + membership."; + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in Intermediate + System to Intermediate Systems (IS-ISs)."; + } + + identity ROUTER_CAPABILITY { + base "ISIS_TLV_TYPE"; + description + "ISIS TLV 242. IS-IS TLV named CAPABILITY, formed of multiple + sub-TLVs, which allows a router to announce its capabilities + within an IS-IS level or the entire routing domain."; + reference + "RFC4971: Intermediate System to Intermediate System (IS-IS) + Extensions for Advertising Router Information."; + } + + //sub-TLVs for TLVs 22, 23, 141, 222, 223 + + identity IS_REACHABILITY_ADMIN_GROUP { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 3. Administrative group(color)."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering"; + } + + identity IS_REACHABILITY_LINK_ID { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 4. Link Local/Remote Identifiers."; + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + } + + identity IS_REACHABILITY_IPV4_INTERFACE_ADDRESS { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 6. IPv4 Interface Address."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering."; + } + + identity IS_REACHABILITY_IPV4_NEIGHBOR_ADDRESS { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 8. IPv4 Neighbor Address."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering."; + } + + identity IS_REACHABILITY_MAX_LINK_BANDWIDTH { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 9. Maximum Link Bandwidth."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering."; + } + + identity IS_REACHABILITY_MAX_RESERVABLE_BANDWIDTH { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 10. Maximum Reservable Bandwidth."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering."; + } + + identity IS_REACHABILITY_UNRESERVED_BANDWIDTH { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 11. Unreserved bandwidth."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering."; + } + + identity IS_REACHABILITY_IPV6_INTERFACE_ADDRESS { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 12. IPv6 Interface Address."; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS."; + } + + identity IS_REACHABILITY_IPV6_NEIGHBOR_ADDRESS { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 13. IPv6 Neighbor Address."; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS."; + } + + identity IS_REACHABILITY_EXTENDED_ADMIN_GROUP { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 14. Extended Administrative Group."; + reference + "RFC7308: Extended Administrative Groups in MPLS Traffic + Engineering (MPLS-TE)."; + } + + identity IS_REACHABILITY_TE_DEFAULT_METRIC { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 18. TE Default Metric."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering."; + } + + identity IS_REACHABILITY_LINK_ATTRIBUTES { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 19. Link Attributes."; + reference + "RFC5209: Definition of an IS-IS Link Attribute Sub-TLV."; + } + + identity IS_REACHABILITY_LINK_PROTECTION_TYPE { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 20. Link Protection Type."; + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS)"; + } + + identity IS_REACHABILITY_BANDWIDTH_CONSTRAINTS { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 22. Bandwidth Constraints."; + reference + "RFC4124: Protocol Extensions for Support of Diffserv-aware MPLS + Traffic Engineering."; + } + + identity IS_REACHABILITY_UNCONSTRAINED_LSP { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 23. Unconstrained LSP."; + reference + "RFC5330: A Link-Type sub-TLV to Convey the Number of Traffic + Engineering Label Switched Paths Signalled with Zero + Reserved Bandwidth across a Link."; + } + + identity IS_REACHABILITY_ADJ_SID { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 31. Adjacency Segment Identifier."; + reference + "draft-ietf-isis-segment-routing-extensions."; + } + + identity IS_REACHABILITY_ADJ_LAN_SID { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 32. Adjacency LAN Segment Identifier."; + reference + "draft-ietf-isis-segment-routing-extensions."; + } + + identity IS_REACHABILITY_LINK_DELAY { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 33. Unidirectional Link Delay."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + identity IS_REACHABILITY_MIN_MAX_LINK_DELAY { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 34. Min/Max Unidirectional Link Delay."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + identity IS_REACHABILITY_LINK_DELAY_VARIATION { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 35. Unidirectional Link Delay Variation."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + identity IS_REACHABILITY_LINK_LOSS { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 36. Unidirectional Link Loss Delay."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + identity IS_REACHABILITY_RESIDUAL_BANDWIDTH { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 37. Unidirectional Residual Bandwidth."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + identity IS_REACHABILITY_AVAILABLE_BANDWIDTH { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 38. Unidirectional Available Bandwidth."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + identity IS_REACHABILITY_UTILIZED_BANDWIDTH { + base "IS_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 39. Unidirectional Utilized Bandwidth."; + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric Extensions."; + } + + //sub-TLVs for TLVs 135, 235, 236, 237 + identity IP_REACHABILITY_TAG { + base "IP_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 1. 32-bit Administrative Tag."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 and IPv6 + Reachability."; + } + + identity IP_REACHABILITY_TAG64 { + base "IP_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 2. 64-bit Administrative Tag."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 and IPv6 + Reachability."; + } + + identity IP_REACHABILITY_PREFIX_SID { + base "IP_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 3. Prefix Segment Identifier."; + reference + "draft-ietf-isis-segment-routing-extension."; + } + + identity IP_REACHABILITY_PREFIX_FLAGS { + base "IP_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 4. Prefix Attribute Flags."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 and IPv6 + Reachability."; + } + + identity IP_REACHABILITY_IPV4_ROUTER_ID { + base "IP_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 11. IPv4 Source Router ID."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 and IPv6 + Reachability."; + } + + identity IP_REACHABILITY_IPV6_ROUTER_ID { + base "IP_REACHABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 12. IPv6 Source Router ID."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 and IPv6 + Reachability."; + } + + + //sub-TLVs for TLVs 242 + + identity ROUTER_CAPABILITY_SR_CAPABILITY { + base "ROUTER_CAPABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 2. Segment Routing Capability."; + reference + "draft-ietf-isis-segment-routing-extensions."; + } + + identity ROUTER_CAPABILITY_SR_ALGORITHM { + base "ROUTER_CAPABILITY_SUBTLVS_TYPE"; + description + "sub-TLV 19. Segment Routing Algorithm."; + reference + "draft-ietf-isis-segment-routing-extensions."; + } + +} diff --git a/models/yang/common/openconfig-isis-lsp.yang b/models/yang/common/openconfig-isis-lsp.yang new file mode 100644 index 0000000000..86f5950c04 --- /dev/null +++ b/models/yang/common/openconfig-isis-lsp.yang @@ -0,0 +1,3604 @@ +submodule openconfig-isis-lsp { + + belongs-to openconfig-isis { + prefix oc-isis; + } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-inet-types { prefix "inet"; } + import openconfig-isis-types { prefix "oc-isis-types"; } + import openconfig-isis-lsdb-types { prefix "oc-isis-lsdb-types"; } + import openconfig-types { prefix "oc-types"; } + import openconfig-mpls-types { prefix "oc-mpls-types"; } + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net "; + + description + "This sub-module describes a YANG model for the IS-IS Link State + Database (LSDB). + + Portions of this code were derived from IETF RFCs relating to the + IS-IS protocol. + Please reproduce this note if possible. + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "0.4.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements."; + reference "0.4.1"; + } + + revision "2018-05-14" { + description + "Update LSDB model to correct Extended IS reach TLV + bug. This change is backwards incompatible due to + adding an additional level of hierarchy to support + multiple instances of the TLV."; + reference "0.4.0"; + } + + revision "2017-07-26" { + description + "Update LSDB and fix bugs."; + reference "0.3.2"; + } + + revision "2017-05-15" { + description + "Refactor LSDB."; + reference "0.3.0"; + } + + revision "2017-01-13" { + description + "Remove top-level /isis container"; + reference "0.2.1"; + } + + revision "2016-12-15" { + description + "Add segment routing to IS-IS module"; + reference "0.2.0"; + } + + revision "2016-10-18" { + description + "Initial revision of IS-IS models."; + reference "0.1.0"; + } + + typedef isis-metric-flags { + type enumeration { + enum INTERNAL { + description + "When this flag is not set, internal metrics are in use."; + } + enum UNSUPPORTED { + description + "When this flag (referred to as the S-bit) is set, then + the metric is unsupported."; + } + } + description + "Type definition for flags used in IS-IS metrics"; + } + + grouping isis-lsdb-link-characteristics-a-bit { + description + "Definition of the A bit, as used in IS-IS link delay TLVs."; + + leaf a-bit { + type boolean; + description + "The A bit is set when the measured value of this parameter + exceeds its configured maximum threshold. The A bit is cleared + when the measured value falls below its configured reuse + threshold."; + } + } + + grouping isis-lsdb-tlv-nlpid-state { + description + "NLP ID parameters for IS-IS."; + + leaf-list nlpid { + type enumeration { + enum IPV4 { + description "IPv4 Address family."; + } + enum IPV6 { + description "IPv6 Address family."; + } + } + description + "Protocol supported. IPv4 is defined as (0xcc) and IPv6 - + (0x8e)"; + reference + "RFC1195: Use of OSI IS-IS for Routing in TCP/IP and + Dual Environments. TLV 129. "; + } + } + + grouping isis-lsdb-subtlv-type-state { + description + "Per-subTLV type operational state parameters for ISIS."; + + leaf type { + type identityref { + base oc-isis-lsdb-types:ISIS_SUBTLV_TYPE; + } + description + "The type of subTLV being described. The type of subTLV is + expressed as a canonical name."; + } + } + + grouping isis-lsdb-tlv-type-state { + description + "Per-subTLV type operational state parameters for ISIS."; + + leaf type { + type identityref { + base oc-isis-lsdb-types:ISIS_TLV_TYPE; + } + description + "The type of TLV being described. The type of TLV is + expressed as a canonical name."; + } + } + + grouping is-reachability-neighbor-state { + description + "This grouping defines is-reachability neighbor."; + + container subtlvs { + description + "This container describes IS Neighbor sub-TLVs."; + + list subtlv { + key "type"; + + description + "List of subTLV types in the LSDB for the specified TLV."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the sub-TLV type."; + } + + container state { + description + "State parameters of IS neighbor state"; + + uses isis-lsdb-subtlv-type-state; + } + + container admin-group { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_ADMIN_GROUP'" { + description + "Only include the administrative group container when + the sub-TLV is type 3"; + } + description + "This container defines sub-TLV 3."; + + container state { + description + "State parameters of sub-TLV 3."; + + leaf-list admin-group { + type uint32; + description + "The administrative group sub-TLV contains a 4-octet + bit mask assigned by the network administrator. Each + set bit corresponds to one administrative group + assigned to the interface. By convention, the least + significant bit is referred to as group 0, and the + most significant bit is referred to as group 31."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. + sub-TLV 3: TLV 22,23,141,222, 223."; + } + } + } + + container link-id { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_LINK_ID'" { + description + "Only include the link identifier container when the + sub-TLV is type 4"; + } + description + "This container defines sub-TLV 4."; + + container state { + description + "State parameters of sub-TLV 4."; + + leaf local { + type uint32; + description + "The value field of this sub-TLV contains 4 octets of + Link Local Identifier followed by 4 octets of Link + Remote Identifier."; + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS). sub-TLV 3: TLV + 22,23,141,222, 223."; + } + + leaf remote { + type uint32; + description + "If the Link Remote Identifier is unknown, it is set + to 0."; + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS). sub-TLV 3: TLV + 22,23,141,222, 223."; + } + } + } + + container ipv4-interface-address { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_IPV4_INTERFACE_ADDRESS'" { + description + "Only include the IPv4 interface address group container + when the sub-TLV is type 6"; + } + description + "This container defines sub-TLV 6."; + + container state { + description + "State parameters of sub-TLV 6."; + + leaf-list address { + type inet:ipv4-address; + description + "A 4-octet IPv4 address for the interface described by + the (main) TLV. This sub-TLV can occur multiple + times."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. + sub-TLV 6: TLV 22,23,41,222,223."; + } + } + } + + container ipv4-neighbor-address { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_IPV4_NEIGHBOR_ADDRESS'" { + description + "Only include the IPv4 neighbor address container when + the sub-TLV is type 8."; + } + description + "This container defines sub-TLV 8."; + + container state { + description + "State parameters of sub-TLV 8."; + + leaf-list address { + type inet:ipv4-address; + description + "A single IPv4 address for a neighboring router on + this link. This sub-TLV can occur multiple times."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. + sub-TLV 8: TLV 22,23, 141,222,223."; + } + } + } + + container max-link-bandwidth { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_MAX_LINK_BANDWIDTH'" { + description + "Only include the maximum link bandwidth container when + the sub-TLV is type 9."; + } + description + "This container defines sub-TLV 9."; + + container state { + description + "State parameters of sub-TLV 9."; + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The maximum bandwidth that can be used on this link + in this direction (from the system originating the LSP + to its neighbors). It is encoded in 32 bits in IEEE + floating point format. The units are bytes (not + bits!) per second."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. + sub-TLV 9: TLV 22,23,141,222,223."; + } + } + } + + container max-reservable-link-bandwidth { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_MAX_RESERVABLE_BANDWIDTH'" { + description + "Only include the maximum reservable link bandwidth + container when the sub-TLV type is 10."; + } + description + "This container defines sub-TLV 10."; + + container state { + description + "State parameters of sub-TLV 10."; + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The maximum amount of bandwidth that can be reserved + in this direction on this link. Note that for + oversubscription purposes, this can be greater than + the bandwidth of the link. It is encoded in 32 bits + in IEEE floating point format. The units are bytes + (not bits!) per second."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. + Sub-TLV 10: TLV 22,23,141,222,223."; + } + } + } + + container unreserved-bandwidth { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_UNRESERVED_BANDWIDTH'" { + description + "Only include the unreserved bandwidth container when + the sub-TLV type is 11."; + } + description + "This container defines unreserved-bandwidth. The units + are bytes per second."; + + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. sub- + TLV 11: TLV 22,23,141,222,223"; + + list setup-priority { + key "priority"; + + leaf priority { + type leafref { + path "../state/priority"; + } + description + "Reference to the setup priority to which the + unreserved bandwidth corresponds."; + } + + description + "Setup priority (0 through 7) for unreserved + bandwidth."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 11."; + + leaf priority { + type uint8 { + range "0..7"; + } + description + "Setup priority level of 0 through 7 to be used by + Unreserved Bandwidth sub-TLV 11."; + } + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The amount of bandwidth reservable in this + direction on this link. Note that for + oversubscription purposes, this can be greater than + the bandwidth of the link. It contains eight 32-bit + IEEE floating point numbers(one for each priority). + The units are bytes (not bits!) per second. The + values correspond to the bandwidth that can be + reserved with a setup priority of 0 through 7, + arranged in increasing order with priority 0 + occurring at the start of the sub-TLV, and priority + 7 at the end of the sub-TLV."; + } + } + } + } + + container ipv6-interface-address { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_IPV6_INTERFACE_ADDRESS'" { + description + "Only include the IPv6 interface address when the + sub-TLV type is 12."; + } + description + "This container defines sub-TLV 12."; + + container state { + description + "State parameters of sub-TLV 12."; + + leaf-list address { + type inet:ipv6-address; + description + "Contains a 16-octet IPv6 address for the interface + described by the containing Extended IS Reachability + TLV. This sub-TLV can occur multiple times."; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS. sub-TLV + 12: TLV 22,23,141,222,223."; + } + } + } + + container ipv6-neighbor-address { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_IPV6_NEIGHBOR_ADDRESS'" { + description + "Only include the IPv6 neighbor address when the + sub-TLV type is 13."; + } + description + "This container defines sub-TLV 13."; + + container state { + description + "State parameters of sub-TLV 13."; + + leaf-list address { + type inet:ipv6-address; + description + "Contains a 16-octet IPv6 address for a neighboring + router on the link described by the (main) TLV. This + sub-TLV can occur multiple times."; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS. sub-TLV + 13: ISIS TLV 22,23,141,222,223."; + } + } + } + + container extended-admin-group { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_EXTENDED_ADMIN_GROUP'" { + description + "Only include the extended admin group when the + sub-TLV type is 14."; + } + description + "This container defines sub-TLV 14."; + container state { + description + "State parameters of sub-TLV 14."; + + leaf-list extended-admin-group { + type uint32; + description + "The extended-admin-group sub-TLV is used in addition + to the Administrative Groups when it is desirable to + make more than 32 colors available for advertisement + in a network."; + reference + "RFC7308: Extended Administrative Groups in MPLS + Traffic Engineering (MPLS-TE). sub-TLV 14: TLV + 22,23,141,222,223."; + } + } + } + + container te-default-metric { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_TE_DEFAULT_METRIC'" { + description + "Only include the default traffic engineering metric + container when the sub-TLV type is 18."; + } + description + "This container defines sub-TLV 18."; + container state { + description + "State parameters of sub-TLV 18."; + + leaf metric { + type uint32; + description + "This metric is administratively assigned and can be + used to present a differently weighted topology to + traffic engineering SPF calculations. To preclude + overflow within a traffic engineering SPF + implementation, all metrics greater than or equal to + MAX_PATH_METRIC SHALL be considered to have a metric + of MAX_PATH_METRIC."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. + sub-TLV 18: TLV 22,23,141,222,223."; + } + } + } + + container link-attributes { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_LINK_ATTRIBUTES'" { + description + "Only include the link attributes container when the + sub-TLV is type 19."; + } + description + "This container defines link-attributes."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 19."; + + leaf-list local-protection { + type enumeration { + enum LOCAL_PROTECTION { + description + "If set, local protection is available for the + link."; + } + enum LINK_EXCLUDED { + description + "If set, the link is excluded from local + protection."; + } + } + description + "Link local-protection attributes."; + + reference + "RFC5029: Definition of an IS-IS Link Attribute Sub- + TLV. TLV 22, sub-TLV 19."; + } + } + } + + container link-protection-type { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_LINK_PROTECTION_TYPE'" { + description + "Only include the link protection type container when + the sub-TLV type 20."; + } + description + "ISIS LSDB parameters relating to the type of link + protection offered."; + + container state { + description + "State parameters of sub-TLV 20."; + + leaf-list type { + type enumeration { + enum EXTRA_TRAFFIC { + description + "If set the link has extra traffic protection. If + the link is of type Extra Traffic, it means that + the link is protecting another link or links. The + LSPs on a link of this type will be lost if any of + the links it is protecting fail."; + } + enum UNPROTECTED { + description + "If set, the link is unprotected. If the link is + of type Unprotected, it means that there is no + other link protecting this link. The LSPs on a + link of this type will be lost if the link + fails."; + } + enum SHARED { + description + "If set, the link has shared protection. If the + link is of type Shared, it means that there are + one or more disjoint links of type Extra Traffic + that are protecting this link. These Extra + Traffic links are shared between one or more links + of type Shared."; + } + enum ONE_ONE { + description + "If set, the link has dedicated 1:1 protection. If + the link is of type Dedicated 1:1, it means that + there is one dedicated disjoint link of type Extra + Traffic that is protecting this link."; + } + enum PLUS_ONE { + description + "If set, the link has dedicated 1+1 protection. If + the link is of type Dedicated 1+1, it means that a + dedicated disjoint link is protecting this link. + However, the protecting link is not advertised in + the link state database and is therefore not + available for the routing of LSPs."; + } + enum ENHANCED { + description + "If set the link has enhanced protection. If the + link is of type Enhanced, it means that a + protection scheme that is more reliable than + Dedicated 1+1, e.g., 4 fiber BLSR/MS-SPRING, is + being used to protect this link."; + } + } + description + "Link protection capabilities."; + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS). sub-TLV 20: + TLV 22,23,141,222,223."; + } + } + } + + container bandwidth-constraints { + when "../state/type =" + + "'oc-isis-lsdb-types:IS_REACHABILITY_BANDWIDTH_CONSTRAINTS'" { + description + "Only include the bandwidth constraints container when + the sub-TLV is type 22."; + } + description + "This container defines bandwidth-constraints. For DS-TE, + the existing Maximum Reservable link bandwidth parameter + is retained, but its semantics is generalized and + interpreted as the aggregate bandwidth constraint across + all Class-Types"; + + reference + "RFC4124: Protocol Extensions for Support of Diffserv- + aware MPLS Traffic Engineering. sub-TLV 22: TLV 22, 23, + 141, 222,223"; + + list bandwidth-constraint { + key "model-id"; + + description + "List of the Bandwidth Constraints sub-TLV instances + present in the TLV."; + + leaf model-id { + type leafref { + path "../state/model-id"; + } + description + "Reference to the model ID associated with the + instance of the Bandwidth Constraints sub-TLV."; + } + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 22."; + + leaf model-id { + type uint8; + description + "Identifier for the Bandwidth Constraints Model + currently in use by the LSR initiating the IGP + advertisement."; + } + } + + container constraints { + description + "Constraints contained within the Bandwidth + Constraints sub-TLV"; + + list constraint { + key "constraint-id"; + + description + "List of the constraints within the Bandwidth + Constraints sub-TLV. The BC0 level is indicated by + the constraint-id leaf being set to 0, with BCN + being indicated by constraint-id N."; + + leaf constraint-id { + type leafref { + path "../state/constraint-id"; + } + description + "Reference to the unique ID for the BCN level."; + } + + container state { + description + "Operational state parameters of the BCN level"; + + leaf constraint-id { + type uint32; + description + "Unique reference for the bandwidth constraint level. BC0 + is indicated by this leaf being set to zero, with BCN + represented by this leaf being set to N."; + } + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The bandwidth constraint, expressed as a 32-bit IEEE + floating point number expressed in bytes per second."; + } + } + } + } + } + } + + container unconstrained-lsp { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_UNCONSTRAINED_LSP'" { + description + "Only include the unconstrained LSP container when the + sub-TLV is type 23."; + } + description + "This container defines sub-TLV 23."; + container state { + description + "State parameters of sub-TLV 23."; + + uses isis-lsdb-subtlv-type-state; + + leaf count { + type uint16; + description + "Unconstrained TE LSP count(TE Label Switched Paths + (LSPs) signalled with zero bandwidth)."; + reference + "RFC5330: A Link-Type sub-TLV to Convey the Number of + Traffic Engineering Label Switched Paths Signalled + with Zero Reserved Bandwidth across a Link. sub-TLV + 23: TLV 22,23,141,222,223"; + } + } + } + + container adjacency-sids { + when "../state/type = 'oc-isis-lsdb-types:IS_REACHABILITY_ADJ_SID'" { + description + "Only include the adjacency SIDs container when the + sub-TLV type is 31"; + } + + description + "This container defines segment routing adjacency SIDs."; + + list adjacency-sid { + key "value"; + + description + "Adjacency Segment-IDs List. An IGP-Adjacency Segment is + an IGP segment attached to a unidirectional adjacency or + a set of unidirectional adjacencies. By default, an IGP- + Adjacency Segment is local to the node which advertises + it."; + + leaf value { + type leafref { + path "../state/value"; + } + description + "Reference to the value of the Adjacency-SID."; + } + + container state { + description + "State parameters of Adjacency-SID."; + + leaf value { + type uint32; + description + "Adjacency-SID value."; + } + + leaf-list flags { + type enumeration { + enum ADDRESS_FAMILY { + description + "Address-family flag. When unset, the Adj-SID + refers to an adjacency with outgoing IPv4 + encapsulation. If set then the Adj-SID refers to + an adjacency with outgoing IPv6 encapsulation."; + } + enum BACKUP { + description + "Backup flag. When set, the Adj-SID refers to an + adjacency being protected (e.g.: using IPFRR or + MPLS-FRR)."; + } + enum VALUE { + description + "Value flag. When set, the SID carries a value + (instead of an index). By default the flag is + SET."; + } + enum LOCAL { + description + "Local flag. When set, the value/index carried + by the SID has local significance. By default + the flag is SET."; + } + enum SET { + description + "Set flag. When set, the S-Flag indicates that + the Adj-SID refers to a set of adjacencies."; + } + } + description + "Flags associated with Adj-Segment-ID."; + } + + leaf weight { + type uint8; + description + "Value that represents the weight of the Adj-SID for + the purpose of load balancing."; + } + } + } + + reference + "draft-ietf-isis-segment-routing-extensions. sub-TLV 31: + TLV 22, 222, 223, 141. "; + } + + container lan-adjacency-sids { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_ADJ_LAN_SID'" { + description + "Only include the LAN adjacency SID container when + the sub-TLV is type 32."; + } + description + "This container defines segment routing LAN adjacency + SIDs"; + + list lan-adjacency-sid { + key "value"; + + description + "Adjacency Segment-IDs List. An IGP-Adjacency Segment is + an IGP segment attached to a unidirectional adjacency or + a set of unidirectional adjacencies. By default, an IGP- + Adjacency Segment is local to the node which advertises + it."; + + leaf value { + type leafref { + path "../state/value"; + } + description + "Reference to the value of the LAN Adjacency-SID."; + } + + container state { + description + "State parameters of LAN Adjacency-SID."; + + leaf value { + type uint32; + description + "LAN Adjacency-SID value."; + } + + leaf-list flags { + type enumeration { + enum ADDRESS_FAMILY { + description + "Address-family flag. When unset, the Adj-SID + refers to an adjacency with outgoing IPv4 + encapsulation. If set then the Adj-SID refers to + an adjacency with outgoing IPv6 encapsulation."; + } + enum BACKUP { + description + "Backup flag. When set, the Adj-SID refers to an + adjacency being protected (e.g.: using IPFRR or + MPLS-FRR)."; + } + enum VALUE { + description + "Value flag. When set, the SID carries a value + (instead of an index). By default the flag is + SET."; + } + enum LOCAL { + description + "Local flag. When set, the value/index carried + by the SID has local significance. By default + the flag is SET."; + } + enum SET { + description + "Set flag. When set, the S-Flag indicates that + the Adj-SID refers to a set of adjacencies."; + } + } + description + "Flags associated with LAN-Adj-Segment-ID."; + } + + leaf weight { + type uint8; + description + "Value that represents the weight of the Adj-SID + for the purpose of load balancing."; + } + + leaf neighbor-id { + type oc-isis-types:system-id; + description + "System ID of the neighbor associated with the LAN- + Adj-Segment-ID value."; + } + } + } + + reference + "draft-ietf-isis-segment-routing-extensions. sub-TLV 32: + TLV 22, 222, 223, 141."; + } + + container link-delay { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_LINK_DELAY'" { + description + "Include the link delay container only when the sub-TLV + type is type 33."; + } + description + "This container defines unidirectional link delay."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 33: TLV 22, 23, 141, 222, 223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 33."; + + uses isis-lsdb-link-characteristics-a-bit; + + leaf delay { + type uint32; + units microseconds; + description + "Average link delay value (in microseconds) between + two directly connected IS-IS neighbors over a + configurable interval."; + } + } + } + + container min-max-link-delay { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_MIN_MAX_LINK_DELAY'" { + description + "Only include the min/max link delay container when the + sub-TLV is type 34."; + } + description + "This container defines min/max link delay."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 34: TLV 22, 23, 141, 222, 223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 34."; + + uses isis-lsdb-link-characteristics-a-bit; + + leaf min-delay { + type uint32; + units microseconds; + description + "Minimum measured link delay value(in microseconds) + between two directly connected IS-IS neighbors over a + configurable interval."; + } + + leaf max-delay { + type uint32; + units microseconds; + description + "Maximum measured link delay value(in microseconds) + between two directly connected IS-IS neighbors over a + configurable interval."; + } + } + } + + container link-delay-variation { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_LINK_DELAY_VARIATION'" { + description + "Only include the link delay variation container when + the sub-TLV is type 35."; + } + description + "This container defines unidirectional link delay + variation."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 35: TLV 22,23,141,222,223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 35."; + + leaf delay { + type uint32; + units microseconds; + description + "Average link delay between two directly connected IS- + IS neighbors over a configurable interval."; + } + } + } + + container link-loss { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_LINK_LOSS'" { + description + "Only include the link loss container when the sub-TLV + is type 36."; + } + description + "This container defines unidirectional link loss delay."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 36: TLV 22, 23, 141, 222, 223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 36."; + + uses isis-lsdb-link-characteristics-a-bit; + + leaf link-loss { + type uint32; + description + "Link packet loss as a percentage of the total traffic + sent over a configurable interval. The basic unit is + 0.000003%, where (2^24 - 2) is 50.331642%. This value + is the highest packet-loss percentage that can be + expressed (the assumption being that precision is more + important on high-speed links than the ability to + advertise loss rates greater than this, and that high- + speed links with over 50% loss are unusable). + Therefore, measured values that are larger than the + field maximum SHOULD be encoded as the maximum + value."; + } + } + } + + container residual-bandwidth { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_RESIDUAL_BANDWIDTH'" { + description + "Only include the resdiual bandwidth container when the + sub-TLV is type 37."; + } + description + "This container defines unidirectional residual + bandwidth."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 37: TLV 22, 23, 141, 222, 223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 37."; + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "Residual bandwidth on a link,forwarding adjacency + [RFC4206], or bundled link in IEEE floating-point + format with units of bytes per second. For a link or + forwarding adjacency, residual bandwidth is defined to + be the Maximum Bandwidth [RFC5305] minus the bandwidth + currently allocated to RSVP-TE label switched paths. + For a bundled link, residual bandwidth is defined to + be the sum of the component link residual + bandwidths."; + } + } + } + + container available-bandwidth { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_AVAILABLE_BANDWIDTH'" { + description + "Only include the available bandwdith container when the + sub-TLV is type 38."; + } + description + "This container defines unidirectional lavailable + bandwidth."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 38: TLV 22, 23, 141, 222, 223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 38."; + + uses isis-lsdb-subtlv-type-state; + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The available bandwidth on a link, forwarding + adjacency, or bundled link in IEEE floating-point + format with units of bytes per second. For a link or + forwarding adjacency, available bandwidth is defined + to be residual bandwidth minus the measured bandwidth + used for the actual forwarding of non-RSVP-TE label + switched path packets. For a bundled link, available + bandwidth is defined to be the sum of the component + link available bandwidths minus the measured bandwidth + used for the actual forwarding of non-RSVP-TE label + switched path packets. For a bundled link, available + bandwidth is defined to be the sum of the component + link available bandwidths."; + } + } + } + + container utilized-bandwidth { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_REACHABILITY_UTILIZED_BANDWIDTH'" { + description + "Only include the utilized bandwidth container when the + TLV is type 39."; + } + description + "This container defines unidirectional utilized + bandwidth."; + + reference + "RFC7810: IS-IS Traffic Engineering (TE) Metric + Extensions. sub-TLV 39: TLV 22, 23, 141, 222, 223."; + + container state { + description + "State parameters of IS Extended Reachability sub-TLV + 39."; + + uses isis-lsdb-subtlv-type-state; + + leaf bandwidth { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The bandwidth utilization on a link, forwarding + adjacency, or bundled link in IEEE floating-point + format with units of bytes per second. For a link or + forwarding adjacency, bandwidth utilization represents + the actual utilization of the link (i.e., as measured + by the advertising node). For a bundled link, + bandwidth utilization is defined to be the sum of the + component link bandwidth utilizations."; + } + } + } + } + } + + uses isis-lsdb-undefined-subtlv; + } + + grouping isis-lsdb-undefined-tlv { + description + "Grouping for unknown TLVs in the IS-IS LSDB"; + + container undefined-tlvs { + description + "Surrounding container for a list of unknown TLVs."; + + list undefined-tlv { + key "type"; + description + "List of TLVs that are not defined within the model, or are + not recognised by the system."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the undefined TLV's type"; + } + + container state { + description + "State parameters of the undefined TLV."; + + uses undefined-tlv-state; + } + } + } + } + + grouping isis-lsdb-undefined-subtlv { + description + "Grouping for unknown Sub-TLVs in the IS-IS LSDB."; + + container undefined-subtlvs { + description + "This container describes undefined ISIS TLVs."; + + list undefined-subtlv { + key "type"; + + description + "Sub-TLVs that are not defined in the model or not + recognised by system."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the type of the undefined sub-TLV"; + } + + container state { + description + "State parameters of the undefined sub-TLV."; + + uses undefined-subtlv-state; + } + } + } + } + + grouping isis-lsdb-prefix-state { + description + "This grouping defines prefix reachability."; + + container subtlvs { + description + "This container describes IS prefix sub-TLVs."; + + list subtlv { + key "type"; + + description + "List of subTLV types in the LSDB for the specified TLV."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the sub-TLV type"; + } + + container state { + description + "State parameters for a prefix."; + + uses isis-lsdb-subtlv-type-state; + } + + container tag { + when "../state/type = " + + "'oc-isis-lsdb-types:IP_REACHABILITY_TAG'" { + description + "Only include the tag container when the sub-TLV is type + 1."; + } + description + "This container defines sub-TLV 1."; + + container state { + description + "State parameters of sub-TLV 1."; + + leaf-list tag32 { + type uint32; + description + "List of 32-bit tags associated with the prefix. Example + uses of these tags include carrying BGP standard (or + extended) communities and controlling redistribution + between levels and areas, different routing protocols, + or multiple instances of IS-IS running on the same + router."; + reference + "RFC5130: A Policy Control Mechanism in IS-IS Using + Administrative Tags. sub-TLV 1."; + } + } + } + + container tag64 { + when "../state/type = " + + "'oc-isis-lsdb-types:IP_REACHABILITY_TAG64'" { + description + "Only include the tag64 container when the sub-TLV is type + 2."; + } + description + "This container defines sub-TLV 2."; + + container state { + description + "State parameters of sub-TLV 2."; + + leaf-list tag64 { + type uint64; + description + "List of 64-bit tags associated with the prefix. Example + uses of these tags include carrying BGP standard (or + extended) communities and controlling redistribution + between levels and areas, different routing protocols, + or multiple instances of IS-IS running on the same + router."; + reference + "RFC5130: A Policy Control Mechanism in IS-IS Using + Administrative Tags. sub-TLV 2."; + } + } + } + + container flags { + when "../state/type = " + + "'oc-isis-lsdb-types:IP_REACHABILITY_PREFIX_FLAGS'" { + description + "Only include the flags container when the sub-TLV is type + 4."; + } + description + "This container defines sub-TLV 4."; + + container state { + description + "State parameters of sub-TLV 4."; + + uses isis-lsdb-subtlv-type-state; + + leaf-list flags { + type enumeration { + enum EXTERNAL_FLAG { + description + "External prefix flag. Set if the prefix has been + redistributed from another protocol. This includes + the case where multiple virtual routers are + supported and the source of the redistributed prefix + is another IS-IS instance."; + } + enum READVERTISEMENT_FLAG { + description + "Readvertisement flag. Set when the prefix has been + leaked from one level to another (upwards or + downwards)."; + } + enum NODE_FLAG { + description + "Node flag. Set when the prefix identifies the + advertising router, i.e., the prefix is a host + prefix advertising a globally reachable address + typically associated with a loopback address."; + } + } + description + "Additional prefix reachability flags."; + + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 and + IPv6 Reachability. sub-TLV 4."; + } + } + } + + container ipv4-source-router-id { + when "../state/type = " + + "'oc-isis-lsdb-types:IP_REACHABILITY_IPV4_ROUTER_ID'" { + description + "Only include the IPv4 Source Router ID container when + the sub-TLV is type 11."; + } + description + "This container defines sub-TLV 11."; + + container state { + description + "State parameters of sub-TLV 11."; + + uses isis-lsdb-subtlv-type-state; + + leaf router-id { + type inet:ipv4-address; + description + "IPv4 Source router ID address. In cases where the + advertisement is an identifier for the advertising + router (e.g., with the N-flag set in the Prefix + Attribute Flags sub-TLV), it may be useful for other + routers to know the source of the advertisement. When + reachability advertisement is leaked from one level to + another, Router ID advertised is always the Router ID + of the IS-IS instance that originated the + advertisement. This would be true even if the prefix + had been learned from another protocol."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 + and IPv6 Reachability. sub-TLV 11"; + } + } + } + + container ipv6-source-router-id { + when "../state/type = " + + "'oc-isis-lsdb-types:IP_REACHABILITY_IPV6_ROUTER_ID'" { + description + "Only include the IPv6 Source Router ID container when + the sub-TLV is type 12."; + } + description + "This container defines sub-TLV 12."; + + container state { + description + "State parameters of sub-TLV 12."; + + uses isis-lsdb-subtlv-type-state; + + leaf router-id { + type inet:ipv6-address; + description + "IPv6 Source router ID address. In cases where the + advertisement is an identifier for the advertising + router (e.g., with the N-flag set in the Prefix + Attribute Flags sub-TLV), it may be useful for other + routers to know the source of the advertisement. When + reachability advertisement is leaked from one level to + another, Router ID advertised is always the Router ID + of the IS-IS instance that originated the + advertisement. This would be true even if the prefix + had been learned from another protocol."; + reference + "RFC7794: IS-IS Prefix Attributes for Extended IPv4 + and IPv6 Reachability. sub-TLV 12."; + } + } + } + + uses isis-lsdb-prefix-sid-state; + } + } + + uses isis-lsdb-undefined-subtlv; + } + + grouping isis-lsdb-prefix-sid-state { + description + "This grouping defines ISIS Prefix SID."; + + container prefix-sids { + when "../state/type = " + + "'oc-isis-lsdb-types:IP_REACHABILITY_PREFIX_SID'" { + description + "Only include the Prefix SID container when + the sub-TLV is type 3."; + } + description + "This container defines segment routing extensions for + prefixes."; + + reference + "draft-ietf-isis-segment-routing-extensions. sub-TLV 3: TLV + 135, 235, 236, 237."; + + list prefix-sid { + key "value"; + + description + "Prefix Segment-ID list. IGP-Prefix Segment is an IGP segment + attached to an IGP prefix. An IGP-Prefix Segment is global + (unless explicitly advertised otherwise) within the SR/IGP + domain."; + + leaf value { + type leafref { + path "../state/value"; + } + description + "Reference to the value of the prefix SID."; + } + + container state { + description + "State parameters for Prefix-SID."; + + leaf value { + type uint32; + description + "IGP Prefix-SID value."; + } + + leaf-list flags { + type enumeration { + enum READVERTISEMENT { + description + "Readvertisment flag. When set, the prefix to which + this Prefix-SID is attached, has been propagated by + the router either from another level or from + redistribution."; + } + enum NODE { + description + "Node flag. When set, the Prefix-SID refers to the + router identified by the prefix. Typically, the + N-Flag is set on Prefix-SIDs attached to a router + loopback address."; + } + enum NO_PHP { + description + "Penultimate-Hop-Popping flag. When set, then the + penultimate hop MUST NOT pop the Prefix-SID before + delivering the packet to the node that advertised + the Prefix-SID."; + } + enum EXPLICIT_NULL { + description + "Explicit-Null flag. When set, any upstream neighbor + of the Prefix-SID originator MUST replace the + Prefix-SID with a Prefix-SID having an Explicit-NULL + value (0 for IPv4 and 2 for IPv6) before forwarding + the packet."; + } + enum VALUE { + description + "Value flag. When set, the Prefix-SID carries a + value (instead of an index). By default the flag is + UNSET."; + } + enum LOCAL { + description + "Local flag. When set, the value/index carried by + the Prefix-SID has local significance. By default + the flag is UNSET."; + } + } + description + "Flags associated with Prefix Segment-ID."; + } + + leaf algorithm { + type uint8; + description + "Prefix-SID algorithm to be used for path computation."; + } + } + } + } + } + + grouping isis-lsdb-common-metric-specification { + description + "Common definitions of the metric in IS-IS."; + + container default-metric { + description + "This container defines ISIS Default Metric."; + + container state { + description + "State parameters for default-metric."; + + leaf flags { + type enumeration { + enum INTERNAL { + description + "When set to zero, indicates internal metrics."; + } + } + description + "ISIS Default-Metric Flags."; + } + + leaf metric { + type oc-isis-types:narrow-metric; + description + "ISIS default metric value. This is a metric understood by + every Intermediate system in the domain. Each circuit + shall have a positive integral value assigned for this + metric. The value may be associated with any objective + function of the circuit, but by convention is intended to + measure the capacity of the circuit for handling traffic, + for example, its throughput in bits-per-second. Higher + values indicate a lower capacity."; + } + } + } + + container delay-metric { + description + "This container defines the ISIS delay metric."; + + container state { + description + "State parameters of delay-metric."; + + leaf metric { + type oc-isis-types:narrow-metric; + description + "ISIS delay metric value. This metric measures the transit + delay of the associated circuit. It is an optional metric, + which if assigned to a circuit shall have a positive + integral value. Higher values indicate a longer transit + delay."; + } + + leaf-list flags { + type isis-metric-flags; + description + "ISIS Delay Metric Flags."; + } + } + } + + container expense-metric { + description + "This container defines the ISIS expense metric."; + + container state { + description + "State parameters of expense-metric."; + + leaf metric { + type oc-isis-types:narrow-metric; + description + "ISIS expense metric value. This metric measures the + monetary cost of utilising the associated circuit. It is + an optional metric, which if assigned to a circuit shall + have a positive integral value1). Higher values indicate a + larger monetary expense."; + } + + leaf-list flags { + type isis-metric-flags; + description + "ISIS Expense Metric Flags."; + } + } + } + + container error-metric { + description + "This container defines the ISIS error metric."; + + container state { + description + "State parameters of error-metric."; + + leaf metric { + type oc-isis-types:narrow-metric; + description + "ISIS error metric value. This metric measures the + residual error probability of the associated circuit. It + is an optional metric, which if assigned to a circuit + shall have a non-zero value. Higher values indicate a + larger probability of undetected errors on the circuit."; + } + + leaf-list flags { + type isis-metric-flags; + description + "IS-IS error metric flags."; + } + } + } + } + + grouping isis-lsdb-neighbor { + description + "This grouping defines attributes of an ISIS standard + neighbor."; + + container state { + description + "State parameters of IS standard neighbor."; + + leaf system-id { + type oc-isis-types:system-id; + description + "System-ID of IS neighbor."; + } + } + + uses isis-lsdb-common-metric-specification; + + } + + grouping ipv4-prefix-attributes-state { + description + "This group defines attributes of an IPv4 standard prefix."; + + container state { + description + "State parameters of IPv4 standard prefix."; + + leaf up-down { + type boolean; + description + "The up/down bit. Set if a prefix is advertised from a + higher level to a lower level (e.g., level 2 to level 1), + indicating that the prefix has traveled down the hierarchy. + Prefixes that have the up/down bit set may only be + advertised down the hierarchy, i.e., to lower levels. When a + prefix is first injected into IS-IS, the bit is UNSET."; + } + + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 prefix contained within reachability TLVs."; + } + } + + uses isis-lsdb-common-metric-specification; + } + + grouping isis-lsdb-common-mt-id { + description + "Common definition of the multi-topology ID"; + + leaf mt-id { + type uint16 { + range "0..4095"; + } + description + "Multi-topology ID"; + } + } + + grouping ipv4-prefix-extended-state { + description + "This grouping defines attributes of an IPv4 extended prefix."; + + container state { + description + "State parameters of an IPv4 extended prefix."; + uses ipv4-prefix-extended-params-state; + } + + uses isis-lsdb-prefix-state; + } + + grouping ipv4-mt-prefix-extended-state { + description + "State parameters that relate to an IPv4 prefix in a + multi-topology context."; + + container state { + description + "State parameters of an IPv4 extended prefix."; + uses ipv4-prefix-extended-params-state; + uses isis-lsdb-common-mt-id; + } + + uses isis-lsdb-prefix-state; + } + + grouping ipv4-prefix-extended-params-state { + description + "State parameters that relate to an IPv4 prefix"; + + leaf up-down { + type boolean; + description + "The up/down bit. Set if a prefix is advertised from a + higher level to a lower level (e.g., level 2 to level 1), + indicating that the prefix has traveled down the hierarchy. + Prefixes that have the up/down bit set may only be + advertised down the hierarchy, i.e., to lower levels. When a + prefix is first injected into IS-IS, the bit is UNSET."; + } + + leaf s-bit { + type boolean; + description + "The Sub-TLV present bit. If UNSET, the octets of Sub-TLVs + are not present. Otherwise, the bit is set and the octet + following the prefix will contain the length of the Sub-TLV + portion of the structure."; + } + + leaf prefix { + type inet:ipv4-prefix; + description + "IPv4 prefix contained within extended reachability TLVs."; + } + + leaf metric { + type oc-isis-types:wide-metric; + description + "ISIS metric value."; + } + } + + grouping ipv6-prefix-extended-state { + description + "State parameters relating to an IPv6 prefix."; + + container state { + description + "State parameters of IPv6 prefix attributes"; + + uses ipv6-prefix-extended-params-state; + } + + uses isis-lsdb-prefix-state; + } + + grouping ipv6-mt-prefix-extended-state { + description + "State parameters relating to a multi-topology IPv6 + prefix."; + + container state { + description + "State parameters relating an IPv6 prefix attribute"; + uses ipv6-prefix-extended-params-state; + uses isis-lsdb-common-mt-id; + } + + uses isis-lsdb-prefix-state; + } + + grouping ipv6-prefix-extended-params-state { + description + "Common parameters of an IPv6 extended prefix."; + + leaf up-down { + type boolean; + description + "The up/down bit. Set if a prefix is advertised from a + higher level to a lower level (e.g., level 2 to level 1), + indicating that the prefix has traveled down the hierarchy. + Prefixes that have the up/down bit set may only be + advertised down the hierarchy, i.e., to lower levels. When a + prefix is first injected into IS-IS, the bit is UNSET."; + } + + leaf x-bit { + type boolean; + description + "The external bit. Set when the prefix was distributed into + IS-IS from another routing protocol."; + } + + leaf s-bit { + type boolean; + description + "The sub-tlv present bit. If UNSET, the octets of Sub-TLVs + are not present. Otherwise, the bit is set and the octet + following the prefix will contain the length of the Sub-TLV + portion of the structure."; + } + + leaf prefix { + type inet:ipv6-prefix; + description + "IPv6 prefix contained within extended reachability TLVs."; + } + + leaf metric { + type oc-isis-types:wide-metric; + description + "ISIS metric value."; + } + } + + grouping isis-lsdb-common-extisreach-neighbors { + description + "Common structure for the Extended IS Reachability and IS + Reachability Neighbour attributes."; + + container neighbors { + description + "This container describes IS neighbors."; + + list neighbor { + key "system-id"; + description + "This list describes ISIS extended neighbors and + reachability attributes."; + + leaf system-id { + type leafref { + path "../state/system-id"; + } + description + "Reference to the neighboring system's system ID."; + } + + container state { + description + "State parameters corresponding to the extended + neighbour."; + + leaf system-id { + type oc-isis-types:system-id; + description + "System-id of the neighbor."; + } + } + + container instances { + description + "This list contains all instances of an adjacency + between the originating IS and the remote IS. + Multiple instances are used where there are + parallel adjacencies between two systems."; + + list instance { + key "id"; + + description + "Instance of the TLV to the remote IS neighbor."; + + leaf id { + type leafref { + path "../state/id"; + } + description + "Reference to the unique identifier for + the instance of the extended IS + reachability sub-TLV."; + } + + container state { + description + "State parameters of extended neighbor"; + + leaf id { + type uint64; + description + "Unique identifier for the instance of the + TLV for the IS neighbor. The instance + ID is not required to be consistent across + across readvertisements of the LSP."; + } + + leaf metric { + type oc-isis-types:wide-metric; + description + "Metric value."; + } + } + uses is-reachability-neighbor-state; + } + } + } + } + } + + grouping isis-lsdb-mtis-common { + description + "Common grouping for structure used within the multi-topology IS + neighbour and multi-topology IS neighbour attribute TLVs."; + + container neighbors { + description + "MT-IS neigbor attributes."; + + list neighbor { + key "mt-id system-id"; + description + "This container describes IS neighbors."; + + leaf mt-id { + type leafref { + path "../state/mt-id"; + } + description + "Reference to the topology that the neighbor is + within."; + } + + leaf system-id { + type leafref { + path "../state/system-id"; + } + description + "Reference to the System ID of the neighbor."; + } + + container state { + description + "Operational state parameters related to the + MT ISN TLV."; + + uses mt-isis-neighbor-state; + } + + container instances { + description + "This list contains all instances of an adjacency + between the originating and remote IS. Multiple + instances are used to indicate where there are + parallel adjacencies between systems."; + + list instance { + key "id"; + + description + "Instance of TLV-222 between the originating + and remote IS."; + + leaf id { + type leafref { + path "../state/id"; + } + description + "Reference to the unique identifier for the + instance of the multi-topology IS neighbor + TLV instance."; + } + + uses mt-isis-neighbor-instance; + } + } + } + } + } + + grouping mt-isis-neighbor-state { + description + "This grouping defines state parameters that are related to + each neighbour entry for the MT ISN TLV."; + + leaf mt-id { + type uint16 { + range "0..4095"; + } + description + "Identifier of a topology being announced."; + } + + leaf system-id { + type oc-isis-types:system-id; + description + "System-id of the IS neighbor."; + } + } + + grouping mt-isis-neighbor-instance { + description + "This grouping defines list of ISIS multi-topology neighbors for + extended ISIS LSP (multiple system IDs)."; + + container state { + description + "State parameters of MT neighbor."; + + leaf metric { + type oc-isis-types:wide-metric; + description + "ISIS metric value."; + } + + leaf id { + type uint64; + description + "Unique identifier for the TLV instance for the + neighbor. The ID is not required to be consistent + across readvertisements of the LSP."; + } + } + uses is-reachability-neighbor-state; + } + + grouping isis-lsdb-generic-tlv { + description + "Generic TLV encoding grouping."; + + leaf type { + type uint8; + description + "TLV Type."; + } + + leaf length { + type uint8; + description + "TLV length."; + } + + leaf value { + type binary; + description + "TLV value."; + } + } + + grouping undefined-tlv-state { + description + "Generic grouping defining an unknown TLV."; + + uses isis-lsdb-generic-tlv; + } + + grouping undefined-subtlv-state { + description + "Generic grouping defining an unknown sub-TLV."; + + uses isis-lsdb-generic-tlv; + } + + grouping lsp-state { + description + "This grouping defines ISIS LSP state information."; + + leaf lsp-id { + type leafref { + path "../state/lsp-id"; + } + + description + "A reference to the Link State PDU ID."; + } + + container state { + description + "State parameters of Link State PDU."; + + leaf lsp-id { + type oc-isis-types:lsp-id; + description + "LSP ID of the LSP."; + } + + leaf maximum-area-addresses { + type uint8; + description + "Number of area addresses permitted for this ISs area. 0 + indicates the IS only supports three area addresses (by + default). Any number inclusive of 1 and 254 indicates the + number of areas allowed."; + } + + leaf version { + type uint8; + default 1; + description + "PDU version. This is set to 1."; + } + + leaf version2 { + type uint8; + default 1; + description + "PDU version2. This is set to 1"; + } + + leaf id-length { + type uint8; + description + "Length of the ID field of NSAP addresses and NETs used in + this routing domain."; + } + + leaf pdu-type { + type enumeration { + enum LEVEL_1 { + description "This enum describes ISIS level 1 PDU."; + } + enum LEVEL_2 { + description "This enum describes ISIS level 2 PDU."; + } + } + description + "Link State PDU type."; + } + + leaf remaining-lifetime { + type uint16; + units "seconds"; + description + "Remaining lifetime in seconds before the LSP expiration."; + } + + leaf sequence-number { + type uint32; + description + "Sequence number of the LSP."; + } + + leaf checksum { + type uint16; + description + "Checksum of the LSP."; + } + + leaf pdu-length { + type uint16; + description + "Total length of the LSP."; + } + + leaf-list flags { + type enumeration { + enum PARTITION_REPAIR { + description + "When set, the originator supports partition + repair."; + } + enum ATTACHED_ERROR { + description + "When set, the originator is attached to another + area using the referred metric."; + } + enum ATTACHED_EXPENSE { + description + "When set, the originator is attached to another + area using the referred metric."; + } + enum ATTACHED_DELAY { + description + "When set, the originator is attached to another + area using the referred metric."; + } + enum ATTACHED_DEFAULT { + description + "When set, the originator is attached to another + area using the referred metric."; + } + enum OVERLOAD { + description + "When set, the originator is overloaded, and must + be avoided in path calculation."; + } + } + description + "LSP Type-Block flags."; + } + + leaf is-type { + type oc-isis-types:level-number; + description + "Type of neighboring system."; + } + } + + container tlvs { + description + "This container defines Link State PDU State TLVs."; + + list tlv { + key "type"; + + description + "List of TLV types in the LSDB for the specified LSP."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the TLV's type."; + } + + container state { + config false; + description + "Operational state parameters relating to the specified + LSP"; + + uses isis-lsdb-tlv-type-state; + } + + container area-address { + when "../state/type = 'oc-isis-lsdb-types:AREA_ADDRESSES'" { + description + "Include area address parameters only when the TLV type + is TLV 1."; + } + + description + "This container defines TLV 1."; + + container state { + description + "State parameters of ISIS TLV 1."; + + leaf-list address { + type oc-isis-types:area-address; + description + "Area adress(es) of the IS. Set of manual area + addresses of this IS."; + reference + "ISO 10589 Intermediate System to Intermediate System + Intra- Domain Routeing Exchange Protocol for use in + Conjunction with the Protocol for Providing the + Connectionless-mode Network Service (ISO 8473 ) + International Standard 10589: 2002, Second Edition, + 2002. TLV 1."; + } + } + } + + container lsp-buffer-size { + when "../state/type = " + + "'oc-isis-lsdb-types:LSP_BUFFER_SIZE'" { + description + "Include the LSP buffer size parameters only when the + TLV type is TLV 14."; + } + + description + "This container defines TLV 14 - the LSP Buffer Size + TLV."; + + container state { + description + "State parameters of TLV 14."; + + leaf size { + type uint16; + units "bytes"; + description + "The maximum MTU that the advertising system can + receive, expressed in bytes."; + reference + "ISO 10589 Intermediate System to Intermediate System + Intra- Domain Routeing Exchange Protocol for use in + Conjunction with the Protocol for Providing the + Connectionless-mode Network Service (ISO 8473 ) + International Standard 10589: 2002, Second Edition, + 2002. TLV 14."; + } + } + } + + container nlpid { + when "../state/type = 'oc-isis-lsdb-types:NLPID'" { + description + "Include NLPID specification only when the TLV type is + TLV 129."; + } + + description + "This container defines TLV 129."; + + container state { + description + "State parameters of ISIS TLV 129."; + + uses isis-lsdb-tlv-nlpid-state; + } + } + + container hostname { + when "../state/type = 'oc-isis-lsdb-types:DYNAMIC_NAME'" { + description + "Include the dynamic hostname TLV only when the TLV is + type 137."; + } + description + "This container defines TLV 137."; + + container state { + description + "State parameters of ISIS TLV 137."; + + leaf-list hostname { + type string; + description + "Name of the node."; + + reference + "RFC6233: IS-IS Registry Extension for Purges, RFC + 5301: Dynamic Hostname Exchange Mechanism for IS-IS. + TLV 137"; + } + } + } + + container ipv4-interface-addresses { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV4_INTERFACE_ADDRESSES'" { + description + "Include the IPv4 interface addresses TLV only when the + TLV is type 132."; + } + description + "This container defines TLV 132."; + + container state { + description + "State parameters of ISIS TLV 132."; + + leaf-list address { + type inet:ipv4-address; + description + "IPv4 address(es) of the interface corresponding to + the SNPA over which this PDU is to be transmitted."; + reference + "RFC1195: Use of OSI IS-IS for Routing in TCP/IP and + Dual Environments. TLV 132."; + } + } + } + + container ipv6-interface-addresses { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV6_INTERFACE_ADDRESSES'" { + description + "Include the IPv6 interface addresses TLV only when the + TLV is type 232."; + } + description + "This container defines TLV 232."; + + container state { + description + "State parameters of ISIS TLV 232."; + + leaf-list address { + type inet:ipv6-address; + description + "IPv6 interface addresses of the node. MUST contain + only the non-link-local IPv6 addresses assigned to the + IS."; + reference + "RFC5308: Routing IPv6 with IS-IS. TLV 232."; + } + } + } + + container ipv4-te-router-id { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV4_TE_ROUTER_ID'" { + description + "Include the IPv4 traffic engineering router ID TLV only + when the TLV is type 134."; + } + description + "This container defines TLV 134."; + + container state { + description + "State parameters of ISIS TLV 134."; + + leaf-list router-id { + type inet:ipv4-address; + description + "IPv4 Traffic Engineering router ID of the node. For + traffic engineering, it guarantees that we have a + single stable address that can always be referenced in + a path that will be reachable from multiple hops away, + regardless of the state of the node's interfaces."; + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. TLV + 134."; + } + } + } + + container ipv6-te-router-id { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV6_TE_ROUTER_ID'" { + description + "Include the IPv6 traffic engineering router ID TLV only + when the TLV is type 140."; + } + description + "This container defines TLV 140."; + + container state { + description + "State parameters of ISIS TLV 140."; + + leaf-list router-id { + type inet:ipv6-address; + description + "IPv6 Traffic Engineering router ID of the node. For + traffic engineering, it guarantees that we have a + single stable address that can always be referenced in + a path that will be reachable from multiple hops away, + regardless of the state of the node's interfaces."; + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS. TLV + 140."; + } + } + } + + container instance-ids { + when "../state/type = 'oc-isis-lsdb-types:INSTANCE_ID'" { + description + "Include the ISIS Instance Identifier TLV only when the + TLV is type 7."; + } + description + "This container defines ISIS Instance Identifier TLV."; + reference "RFC6822: IS-IS Multi-Instance. TLV 7."; + + list instance-id { + key "instance-id"; + + description + "A list of instance IDs received within TLV 7 within an + IS-IS LSP. In the case that more than one instance of + TLV 7 is included in the LSP, the instance IDs specified + within the instances are concatenated within this + list."; + + leaf instance-id { + type leafref { + path "../state/instance-id"; + } + description + "Reference to the unique instance ID."; + } + container state { + description + "State parameters of ISIS TLV 7."; + + leaf instance-id { + type uint16; + description + "An Instance Identifier (IID) to uniquely identify + an IS-IS instance. When the IID = 0, the list of + supported ITIDs MUST NOT be present. An IID-TLV with + IID = 0 MUST NOT appear in an SNP or LSP. When the + TLV appears (with a non-zero IID) in an SNP or LSP, + exactly one ITID. MUST be present indicating the + topology with which the PDU is associated. If no + ITIDs or multiple ITIDs are present or the IID is + zero, then the PDU MUST be ignored."; + } + + leaf-list topology-id { + type uint16; + description + "Instance-Specific Topology Identifiers (ITIDs)."; + } + } + } + } + + container ipv4-srlgs { + when "../state/type = 'oc-isis-lsdb-types:IPV4_SRLG'" { + description + "Include the IPv4 SRLG TLV only when the TLV is type + 138."; + } + description + "This container defines ISIS SRLG TLV 138."; + + reference + "RFC5307: IS-IS Extensions in Support of Generalized + Multi-Protocol Label Switching (GMPLS). TLV 138."; + + list ipv4-srlg { + key "instance-number"; + + description + "Instance of the IPv4 SRLG TLV"; + + leaf instance-number { + type leafref { + path "../state/instance-number"; + } + description + "Reference to the instance number of TLV 138."; + } + + container state { + description + "State parameters of TLV 138."; + + leaf instance-number { + type uint32; + description + "An arbitrary unsigned 32-bit integer used to + disambiguate the instance of TLV 138. The instance + identifier is synthesised by the system + and may be renumbered for the same SRLG definition + in subsequent advertised LSPs if (and only if) the + entire list of SRLGs is replaced."; + } + + leaf system-id { + type oc-isis-types:system-id; + description + "Neighbor system ID."; + } + + leaf psn-number { + type uint8; + description + "Pseudonode number if the neighbor is on a LAN + interface."; + } + + leaf-list flags { + type enumeration { + enum NUMBERED { + description + "When set, the interface is numbered, whereas if + unset indicates that the interface is + unnumbered."; + } + } + description + "SRLG flags."; + } + + leaf ipv4-interface-address { + type inet:ipv4-address; + description + "IPv4 interface address."; + } + + leaf ipv4-neighbor-address { + type inet:ipv4-address; + description + "IPv4 neighbor address."; + } + + leaf-list srlg-value { + type uint32; + description + "List of SRLG values."; + } + } + } + } + + container ipv6-srlgs { + when "../state/type = 'oc-isis-lsdb-types:IPV6_SRLG'" { + description + "Include the IPv6 SRLG TLV only when the TLV is type + 139."; + } + description + "This container defines ISIS SRLG TLV."; + + reference + "RFC6119: IPv6 Traffic Engineering in IS-IS. TLV 139."; + + list ipv6-srlg { + key "instance-number"; + + description + "Instance of the IPv6 SRLG TLV."; + + leaf instance-number { + type leafref { + path "../state/instance-number"; + } + description + "Reference to the instance number of the IPv6 Shared + Risk Link Group (SRLG) TLV."; + } + + container state { + description + "State parameters of TLV 139."; + + leaf instance-number { + type uint32; + description + "An arbitrary unsigned 32-bit integer used to + disambiguate the instance of TLV 138. The instance + identifier is synthesised by the system + and may be renumbered for the same SRLG definition + in subsequent advertised LSPs if (and only if) the + entire list of SRLGs is replaced."; + } + + leaf system-id { + type oc-isis-types:system-id; + description + "Neighbor system ID."; + } + + leaf psn-number { + type uint8; + description + "Pseudonode number if the neighbor is on a LAN + interface."; + } + + leaf-list flags { + type enumeration { + enum NA { + description + "When set, the IPv6 neighbour address is + included, whereas if unset, it is omitted"; + } + } + description + "IPv6 SRLG flags."; + } + + leaf ipv6-interface-address { + type inet:ipv6-address; + description + "IPv6 interface address or Link Local Identifier."; + } + + leaf ipv6-neighbor-address { + type inet:ipv6-address; + description + "IPv6 neighbor address or Link Remote Identifier."; + } + + leaf-list srlg-value { + type uint32; + description + "SRLG values."; + } + } + } + } + + container purge-oi { + when "../state/type = 'oc-isis-lsdb-types:PURGE_OI'" { + description + "Only include the purge originator identitication TLV + when the TLV type is 13."; + } + description + "This container defines ISIS purge TLV."; + + reference + "RFC6232: Purge Originator Identification TLV for IS-IS. + TLV 13."; + + container state { + description + "State parameters of TLV 13."; + + leaf system-id-count { + type uint8; + description + "Number of system IDs carried in this TLV."; + } + + leaf source-system-id { + type oc-isis-types:system-id; + description + "System ID of the Intermediate System that inserted + this TLV."; + } + + leaf received-system-id { + type oc-isis-types:system-id; + description + "System ID of the Intermediate System from which the + purge was received."; + } + } + } + + container router-capabilities { + when "../state/type = " + + "'oc-isis-lsdb-types:ROUTER_CAPABILITY'" { + description + "Only include the router capability TLV when the TLV is + type 242."; + } + description + "This container defines router capabilities."; + + list capability { + key "instance-number"; + + description + "This list describes IS Router capabilities."; + + reference + "RFC4971: Intermediate System to Intermediate System + (IS-IS) Extensions for Advertising Router Information. + TLV 242."; + + leaf instance-number { + type leafref { + path "../state/instance-number"; + } + description + "Reference to the instance number of the router + capability TLV."; + } + + container state { + description + "State parameters of TLV 242."; + + leaf instance-number { + type uint32; + description + "A unique instance number for the instance of the + router capabilities TLV. The instance number should + be autogenerated by the producer of the data and may + be renumbered if the entire LSP contents are + replaced in subsequent advertisements."; + } + + leaf router-id { + type inet:ipv4-address; + description + "IPv4 router-id."; + } + + leaf-list flags { + type enumeration { + enum FLOOD { + description + "When the S bit is set(1), the IS - IS Router + CAPABILITY TLV MUST be flooded across the entire + routing domain. When the S bit is not set(0), + the TLV MUST NOT be leaked between levels . This + bit MUST NOT be altered during the TLV + leaking."; + } + enum DOWN { + description + "When the IS-IS Router CAPABILITY TLV is leaked + from level - 2 to level-1, the Down bit MUST be + set. Otherwise, this bit MUST be clear. IS - IS + Router capability TLVs with the Down bit set + MUST NOT be leaked from level - 1 to level-2. + This is to prevent TLV looping."; + } + } + description + "Router capability flags."; + } + } + + container subtlvs { + description + "This container describes router capability TLV + sub-TLVs"; + + list subtlv { + key "type"; + description + "List of subTLV types in the LSDB for the specified + TLV"; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the sub-TLV type"; + } + + container state { + description + "State parameters of IS Router Capabilities"; + + uses isis-lsdb-subtlv-type-state; + } + + container segment-routing-algorithms { + when "../state/type = " + + "'oc-isis-lsdb-types:ROUTER_CAPABILITY_SR_ALGORITHM'" { + description + "Only include segment routing algorithm when the + sub-TLV is type 19."; + } + description + "This container defines SR algorithm sub-TLV 19."; + + reference + "draft-ietf-isis-segment-routing-extensions. + TLV 242, sub-TLV 19"; + + container state { + description + "State parameters of sub-TLV 19 - Segment + Routing Algorithm."; + + leaf-list algorithm { + type enumeration { + enum SPF { + value 0; + description + "Shortest Path First (SPF) algorithm + based on link metric. This is the + well-known shortest path algorithm as + computed by the IS-IS Decision process. + Consistent with the deployed practice + for link-state protocols, algorithm 0 + permits any node to overwrite the SPF + path with a different path based on + local policy."; + } + enum STRICT_SPF { + value 1; + description + "Strict Shortest Path First (SPF) + algorithm based on link metric. The + algorithm is identical to algorithm 0 + but algorithm 1 requires that all nodes + along the path will honor the SPF + routing decision. Local policy MUST NOT + alter the forwarding decision computed + by algorithm 1 at the node claiming to + support algorithm 1."; + } + } + description + "The Segment Routing algorithm that is + described by the TLV."; + } + } + } + + container segment-routing-capability { + when "../state/type = " + + "'oc-isis-lsdb-types:ROUTER_CAPABILITY_SR_CAPABILITY'" { + description + "Only include the SR capability sub-TLV when + the sub-TLV type is 2."; + } + description + "This container defines SR Capability sub-TLV 2."; + + reference + "draft-ietf-isis-segment-routing-extensions. TLV + 242, sub-TLV 2."; + + container state { + description + "State parameters of IS SR Router Capability"; + + leaf-list flags { + type enumeration { + enum IPV4_MPLS { + description + "When set, the router is capable of + processing SR MPLS encapsulated IPv4 + packets on all interfaces."; + } + enum IPV6_MPLS { + description + "When set, the router is capable of + processing SR MPLS encapsulated IPv6 + packets on all interfaces."; + } + enum IPV6_SR { + description + "When set, the router is capable of + processing the IPv6 Segment Routing Header + on all interfaces."; + } + } + description + "Segment Routing Capability Flags."; + } + } + + container srgb-descriptors { + description + "SRGB Descriptors included within the SR + capability sub-TLV"; + + list srgb-descriptor { + key "range"; + description + "Descriptor entry within the SR capabilty + sub-TLV"; + + leaf range { + type leafref { + path "../state/range"; + } + description + "Reference to unique SRGB Descriptor."; + } + + container state { + description + "State parameters of the SR range"; + + leaf range { + type uint32; + description + "Number of SRGB elements. The range + value MUST be greater than 0."; + } + + leaf label { + type oc-mpls-types:mpls-label; + description + "The first value of the SRGB when + expressed as an MPLS label."; + } + } + } + } + } + } + } + uses isis-lsdb-undefined-subtlv; + } + } + + container is-reachability { + when "../state/type = 'oc-isis-lsdb-types:IIS_NEIGHBORS'" { + description + "Include IIS_NEIGHBORS sub-TLV when the TLV type is 2."; + } + description + "This container describes list of ISIS neighbors and + attributes."; + + reference + "ISO 10589, Intermediate System to Intermediate System + Intra- Domain Routeing Exchange Protocol for use in + Conjunction with the Protocol for Providing the + Connectionless-mode Network Service (ISO 8473), + International Standard 10589: 2002, Second Edition, + 2002. TLV 2."; + + container neighbors { + description + "This container describes IS neighbors."; + + list neighbor { + key "system-id"; + description + "IS reachability neighbor attributes."; + + leaf system-id { + type leafref { + path "../state/system-id"; + } + description + "Reference to the system ID of the neighbor."; + } + + uses isis-lsdb-neighbor; + } + } + } + + container ipv4-internal-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV4_INTERNAL_REACHABILITY'" { + description + "Include IPv4 internal reachability TLV when the TLV + type is specified as 128."; + } + description + "This container defines list of IPv4 internal reachability + information."; + + reference + "RFC1195: OSI ISIS for IP and Dual Environments. RFC5302: + Domain-Wide Prefix Distribution with Two-Level IS-IS. TLV + 128"; + + container prefixes { + description + "This container describes IS prefixes."; + + list prefix { + key "prefix"; + + description + "IPv4 prefixes and internal reachability attributes."; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the IPv4 prefix"; + } + + uses ipv4-prefix-attributes-state; + } + } + } + + container ipv4-external-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV4_EXTERNAL_REACHABILITY'" { + description + "Include IPv4 external reachability when the TLV type + is set to 130."; + } + description + "This container defines list of IPv4 external reachability + information."; + + reference + "RFC1195: OSI ISIS for IP and Dual Environments. RFC5302: + Domain-Wide Prefix Distribution with Two-Level IS-IS. TLV + 130"; + + container prefixes { + description + "This container describes IS neighbors."; + + list prefix { + key "prefix"; + + description + "IPv4 external prefixes and reachability attributes."; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the IPv4 prefix."; + } + + uses ipv4-prefix-attributes-state; + } + } + } + + container authentication { + when "../state/type = 'oc-isis-lsdb-types:AUTHENTICATION'" { + description + "Only include the authentication TLV when the TLV is + type 10."; + } + description + "This container defines authentication information of the + node."; + + reference + "ISO 10589 Intermediate System to Intermediate System + Intra- Domain Routeing Exchange Protocol for use in + Conjunction with the Protocol for Providing the + Connectionless-mode Network Service (ISO 8473) + International Standard 10589: 2002, Second Edition, 2002. + TLV 10."; + + container state { + description + "State parameters of TLV 10."; + + leaf crypto-type { + type enumeration { + enum HMAC_MD5 { + description + "HMAC-MD5 Authentication type."; + } + enum CLEARTEXT { + description + "Cleartext Authentication type."; + } + } + description + "Authentication type to be used."; + } + + leaf authentication-key { + type string; + description + "Authentication key to be used."; + } + } + } + + container extended-is-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:EXTENDED_IS_REACHABILITY'" { + description + "Only included the extended IS reachability TLV when the + TLV is type 22."; + } + + description + "This container defines list of ISIS extended reachability + neighbors."; + + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. TLV + 22."; + + uses isis-lsdb-common-extisreach-neighbors; + } + + container extended-ipv4-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:EXTENDED_IPV4_REACHABILITY'" { + description + "Only include the extended IPv4 reachability container + when the TLV type is 135."; + } + description + "This container defines list of IPv4 extended reachability + information."; + + reference + "RFC5305: IS-IS Extensions for Traffic Engineering. TLV + 135"; + + container prefixes { + description + "This container describes IS prefixes."; + + list prefix { + key "prefix"; + + description + "This list describes IPv4 extended prefixes and + attributes."; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the IPv4 prefix that the TLV describes + the attributes of."; + } + + uses ipv4-prefix-extended-state; + } + } + } + + container ipv6-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:IPV6_REACHABILITY'" { + description + "Only include the IPv6 reachability container when the + TLV type is 236."; + } + description + "This container defines list of IPv6 reachability + information."; + + reference + "RFC5308: Routing IPv6 with IS-IS. TLV 236"; + + container prefixes { + description + "This container describes IS prefixes."; + + list prefix { + key "prefix"; + + description + "This list defines IPv6 extended prefix attributes."; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the IPv6 prefix that the TLV + corresponds to."; + } + + uses ipv6-prefix-extended-state; + } + } + } + + container multi-topology { + when "../state/type = 'oc-isis-lsdb-types:MULTI_TOPOLOGY'" { + description + "Only include the multi-topology container when the TLV + is type 229."; + } + + description + "This container defines the topology supported."; + + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in + Intermediate System to Intermediate Systems (IS-ISs). TLV + 229"; + + container topologies { + description + "This container describes IS topologies."; + + list topology { + key "mt-id"; + + description + "This list describes a topology."; + + leaf mt-id { + type leafref { + path "../state/mt-id"; + } + description + "Reference to the multi-topology ID being described + by the list entry."; + } + + container state { + description + "State parameters of IS multi-topology TLV 229."; + + leaf mt-id { + type uint16 { + range "0 .. 4095"; + } + description + "Multi-topology ID."; + } + + leaf attributes { + type enumeration { + enum OVERLOAD { + description + "When set, node is overloaded, still part of + the topology but cannot be used for transit."; + } + enum ATTACHED { + description + "When set, node is attached to another area + using the referred metric and can be used as + default gateway."; + } + } + description + "Attributes of the LSP for the associated + topology."; + } + } + } + } + } + + container isis-neighbor-attribute { + when "../state/type = " + + "'oc-isis-lsdb-types:IS_NEIGHBOR_ATTRIBUTE'" { + description + "Only include the neighbor attribute container when the + TLV is type 23."; + } + description + "This container defines list of ISIS topology neighbors + for extended ISIS LSP (multiple system IDs). "; + + reference + "RFC5311: Simplified Extension of Link State PDU (LSP) + Space for IS-IS. TLV 23. It is identical in format to the + extended IS reachability TLV 22."; + + uses isis-lsdb-common-extisreach-neighbors; + } + + container is-alias-id { + when "../state/type = 'oc-isis-lsdb-types:ISIS_ALIAS_ID'" { + description + "Only include the ISIS alias ID container when the TLV + is type 24."; + } + + description + "This container defines the IS-Alias TLV which allows + extension-capable ISs to recognize the Originating System + of an Extended LSP set. It identifies the Normal system- + id of the Originating System."; + + reference + "RFC5311: Simplified Extension of Link State PDU (LSP) + Space for IS-IS TLV 24."; + + container state { + config false; + description + "State parameters of alias ID."; + + leaf alias-id { + type oc-isis-types:system-id; + description + "List of alias ID(s)."; + } + } + } + + container mt-isn { + when "../state/type = 'oc-isis-lsdb-types:MT_ISN'" { + description + "Only include the MT ISN container when the TLV is type + 222."; + } + description + "This container defines list of ISIS multi-topology + neighbors."; + + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in + Intermediate System to Intermediate Systems (IS-ISs). TLV + 222."; + + uses isis-lsdb-mtis-common; + } + + container mt-isis-neighbor-attribute { + when "../state/type = " + + "'oc-isis-lsdb-types:MT_IS_NEIGHBOR_ATTRIBUTE'" { + description + "Only include the MT ISIS neighbor attribute container + when the TLV is type 223."; + } + + description + "This container defines list of ISIS multi-topology + neighbors."; + + reference + "RFC5311: Simplified Extension of Link State PDU (LSP) + Space for IS-IS. TLV 223. It is identical in format to the + MT-ISN TLV 222."; + + uses isis-lsdb-mtis-common; + } + + container mt-ipv4-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:MT_IPV4_REACHABILITY'" { + description + "Only include the multi-topology IPv4 reachability + container when the TLV is type 235."; + } + description + "This container defines list of IPv4 reachability + Information in multi-topology environment."; + + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in + Intermediate System to Intermediate Systems (IS-ISs). TLV + 235."; + + container prefixes { + description + "This container describes IS prefixes."; + + list prefix { + key "mt-id prefix"; + + leaf mt-id { + type leafref { + path "../state/mt-id"; + } + description + "Reference to the topology ID of the topology that + the prefix is within."; + } + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the prefix to which reachability is + being advertised."; + } + + description + "IPv4 prefixes that are contained within MT + reachability TLV."; + + uses ipv4-mt-prefix-extended-state; + } + } + } + + container mt-ipv6-reachability { + when "../state/type = " + + "'oc-isis-lsdb-types:MT_IPV6_REACHABILITY'" { + description + "Only include the multi-topology IPv6 reachability + container when the TLV is type 237."; + } + description + "This container defines list of IPv6 reachability + information in multi - topology environment."; + + reference + "RFC5120: M-ISIS: Multi Topology (MT) Routing in + Intermediate System to Intermediate Systems (IS-ISs). TLV + 237."; + + container prefixes { + description + "This container describes IS prefixes."; + + list prefix { + key "prefix mt-id"; + description + "List of IPv6 prefixes contained within MT + reachability TLV."; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the IPv6 prefix described by the + TLV."; + } + + leaf mt-id { + type leafref { + path "../state/mt-id"; + } + description + "Reference to the multi-topology ID."; + } + + uses ipv6-mt-prefix-extended-state; + } + } + } + } + } + + uses isis-lsdb-undefined-tlv; + } +} diff --git a/models/yang/common/openconfig-isis-routing.yang b/models/yang/common/openconfig-isis-routing.yang new file mode 100644 index 0000000000..9df1c93211 --- /dev/null +++ b/models/yang/common/openconfig-isis-routing.yang @@ -0,0 +1,396 @@ +submodule openconfig-isis-routing { + + belongs-to openconfig-isis { + prefix "oc-isis"; + } + + // import some basic types + import openconfig-isis-types { prefix oc-isis-types; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-mpls-types { prefix oc-mplst; } + import openconfig-segment-routing { prefix oc-sr; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module describes YANG model for ISIS Routing"; + + oc-ext:openconfig-version "0.4.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements."; + reference "0.4.1"; + } + + revision "2018-05-14" { + description + "Update LSDB model to correct Extended IS reach TLV + bug. This change is backwards incompatible due to + adding an additional level of hierarchy to support + multiple instances of the TLV."; + reference "0.4.0"; + } + + revision "2017-07-26" { + description + "Update LSDB and fix bugs."; + reference "0.3.2"; + } + + revision "2017-05-15" { + description + "Refactor LSDB."; + reference "0.3.0"; + } + + revision "2017-01-13" { + description + "Remove top-level /isis container"; + reference "0.2.1"; + } + + revision "2016-12-15" { + description + "Add segment routing to IS-IS module"; + reference "0.2.0"; + } + + revision "2016-10-18" { + description + "Initial revision of IS-IS models."; + reference "0.1.0"; + } + + // extension statements + + // feature statements + + // identity statements + + // typedef statements + + // grouping statements + + grouping rt-admin-config { + description + "Re-usable grouping to enable or disable a particular IS-IS feature."; + + leaf enabled { + type boolean; + description + "When set to true, the functionality within which this leaf is + defined is enabled, when set to false it is explicitly disabled."; + } + } + + grouping isis-afi-safi-config { + description + "This grouping defines Address-Family configuration parameters"; + + leaf afi-name { + type identityref { + base oc-isis-types:AFI_TYPE; + } + description + "Address-family type."; + } + + leaf safi-name { + type identityref { + base oc-isis-types:SAFI_TYPE; + } + description + "Subsequent address-family type."; + } + } + + grouping isis-shortcuts-afi-config { + description + "This grouping defines ISIS Shortcuts configuration parameters"; + + leaf afi-name { + type identityref { + base oc-isis-types:AFI_TYPE; + } + description "Address-family type."; + } + + leaf-list nh-type { + type identityref { + base oc-mplst:PATH_SETUP_PROTOCOL; + } + description "Tunnel NH Type(RSVP,SR). When present it implies + that nh-type shortcut is enabled for a specified AFI."; + } + } + + grouping isis-shortcuts-config { + description + "This grouping defines ISIS Shortcuts consfiguration parameters"; + + container config { + description "This container defines ISIS shortcuts configuration."; + uses rt-admin-config; + } + + container state { + config false; + description "This container defines state for ISIS shortcuts."; + uses rt-admin-config; + } + } + + grouping isis-mt-config { + description + "This grouping defines ISIS multi-topology configuration parameters"; + + leaf afi-name { + type identityref { + base oc-isis-types:AFI_TYPE; + } + description + "Address-family type."; + } + leaf safi-name { + type identityref { + base oc-isis-types:SAFI_TYPE; + } + description + "Subsequent address-family type."; + } + //prefer single topology + } + + + + // *********** STRUCTURE GROUPINGS ********************** + + grouping isis-metric-config { + description + "This grouping defines ISIS metric configuration"; + + leaf metric { + type uint32; + default 10; + description "ISIS metric value(default=10)."; + } + } + + grouping isis-afi-safi-list { + description + "This grouping defines address-family configuration and state + information"; + + list af { + key "afi-name safi-name"; + + description + "Address-family/Subsequent Address-family list."; + + leaf afi-name { + type leafref { + path "../config/afi-name"; + } + description + "Reference to address-family type"; + } + + leaf safi-name { + type leafref { + path "../config/safi-name"; + } + description + "Reference to subsequent address-family type"; + } + + container config { + description + "This container defines AFI-SAFI configuration parameters"; + + uses isis-afi-safi-config; + uses isis-metric-config; + uses rt-admin-config; + } + + container state { + config false; + description + "This container defines AFI-SAFI State information"; + + uses isis-afi-safi-config; + uses isis-metric-config; + uses rt-admin-config; + } + + uses isis-mt-list; + } + } + + grouping isis-if-afi-safi-list { + description + "This grouping defines address-family configuration and state + information"; + + list af { + key "afi-name safi-name"; + + description + "Address-family/Subsequent Address-family list."; + + leaf afi-name { + type leafref { + path "../config/afi-name"; + } + description + "Reference to address-family type"; + } + + leaf safi-name { + type leafref { + path "../config/safi-name"; + } + description + "Reference to subsequent address-family type"; + } + + container config { + description + "This container defines AFI-SAFI configuration parameters. Single + topology is the default setting."; + uses isis-afi-safi-config; + uses isis-metric-config; + uses rt-admin-config; + } + + container state { + config false; + description + "This container defines AFI-SAFI State information"; + uses isis-afi-safi-config; + uses isis-metric-config; + uses rt-admin-config; + } + + uses oc-sr:sr-igp-interface-top; + } + } + + grouping isis-if-global-afi-safi-list { + description + "This grouping defines address-family configuration and state + information"; + + list af { + key "afi-name safi-name"; + + description + "Address-family/Subsequent Address-family list."; + + leaf afi-name { + type leafref { + path "../config/afi-name"; + } + description + "Reference to address-family type"; + } + + leaf safi-name { + type leafref { + path "../config/safi-name"; + } + description + "Reference to subsequent address-family type"; + } + + container config { + description + "This container defines AFI-SAFI configuration parameters. Single + topology is the default setting."; + uses isis-afi-safi-config; + uses rt-admin-config; + } + + container state { + config false; + description + "This container defines AFI-SAFI State information"; + uses isis-afi-safi-config; + uses rt-admin-config; + } + } + } + + grouping isis-shortcuts-afi-list { + description + "This grouping defines ISIS Shorcuts configuration and + state information"; + + list afi { + key "afi-name"; + + description + "Address-family list."; + + leaf afi-name { + type leafref { + path "../config/afi-name"; + } + description + "Reference to address-family type."; + } + + container config { + description + "This container defines ISIS Shortcuts configuration parameters"; + uses isis-shortcuts-afi-config; + } + + container state { + config false; + description + "This container defines ISIS Shortcuts state information"; + uses isis-shortcuts-afi-config; + } + } + } + + grouping isis-mt-list { + description + "This grouping defines multi-topology address-family configuration and + state information. MT0 - IPv4 Unicast, MT2 - IPv6 Unicast, MT3 - + IPv4 Multicast, MT4 - IPv6 Multicast"; + + container multi-topology { + description + "This container defines multi-topology address-family configuration + and state information. ISIS TLV 235, 237."; + + container config { + description + "This container defines AFI-SAFI multi-topology configuration + parameters"; + uses isis-mt-config; + } + + container state { + config false; + description + "This container defines AFI-SAFI multi-topology state information"; + uses isis-mt-config; + uses rt-admin-config; + } + } + } +} diff --git a/models/yang/common/openconfig-isis-types.yang b/models/yang/common/openconfig-isis-types.yang new file mode 100644 index 0000000000..a35caf4ca7 --- /dev/null +++ b/models/yang/common/openconfig-isis-types.yang @@ -0,0 +1,356 @@ +module openconfig-isis-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/isis-types"; + + prefix "oc-isis-types"; + + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module contains general data definitions for use in ISIS YANG + model."; + + oc-ext:openconfig-version "0.4.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.2"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.1"; + } + + revision "2018-05-14" { + description + "Update LSDB model to correct Extended IS reach TLV + bug. This change is backwards incompatible due to + adding an additional level of hierarchy to support + multiple instances of the TLV."; + reference "0.4.0"; + } + + revision "2017-07-26" { + description + "Update LSDB and fix bugs."; + reference "0.3.2"; + } + + revision "2017-05-15" { + description + "Refactor LSDB."; + reference "0.3.0"; + } + + revision "2017-01-13" { + description + "Remove top-level /isis container"; + reference "0.2.1"; + } + + revision "2016-12-15" { + description + "Add segment routing to IS-IS module"; + reference "0.2.0"; + } + + revision "2016-10-18" { + description + "Initial revision of IS-IS models."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + identity OVERLOAD_RESET_TRIGGER_TYPE { + description + "Base identify type for triggers that reset Overload Bit"; + } + + identity WAIT_FOR_BGP { + base OVERLOAD_RESET_TRIGGER_TYPE; + description + "Base identity type for resetting Overload Bit when BGP has converged. "; + } + + identity WAIT_FOR_SYSTEM { + base OVERLOAD_RESET_TRIGGER_TYPE; + description + "Base identity type for resetting Overload Bit when system resources have + been restored. "; + } + + identity MT_TYPE { + description + "Base identify type for multi-topology"; + } + + identity SAFI_TYPE { + description + "Base identify type for SAFI"; + } + + identity AFI_TYPE { + description + "Base identify type for AFI"; + } + + identity AFI_SAFI_TYPE { + description + "Base identify type for AFI/SAFI"; + } + + identity IPV4_UNICAST { + base AFI_SAFI_TYPE; + description + "Base identify type for IPv4 Unicast address family"; + } + + identity IPV6_MULTICAST { + base AFI_SAFI_TYPE; + description + "Base identify type for IPv6 multicast address family"; + } + + identity IPV4_MULTICAST { + base AFI_SAFI_TYPE; + description + "Base identify type for IPv4 multicast address family"; + } + + identity IPV6_UNICAST { + base AFI_SAFI_TYPE; + description + "Base identify type for IPv6 unicast address family"; + } + + identity UNICAST { + base SAFI_TYPE; + description + "Base identify type for IPv4 Unicast address family"; + } + + identity MULTICAST { + base SAFI_TYPE; + description + "Base identify type for IPv6 multicast address family"; + } + + identity IPV4 { + base AFI_TYPE; + description + "Base identify type for IPv4 address family"; + } + + identity IPV6 { + base AFI_TYPE; + description + "Base identify type for IPv6 address family"; + } + + // typedef statements + typedef level-type { + type enumeration { + enum LEVEL_1 { + description "This enum describes ISIS level 1"; + } + enum LEVEL_2 { + description "This enum describes ISIS level 2"; + } + enum LEVEL_1_2 { + description "This enum describes ISIS level 1-2"; + } + } + description + "This type defines ISIS level types"; + } + + typedef level-number { + type uint8 { + range "1..2"; + } + description + "This type defines ISIS level."; + } + + typedef adaptive-timer-type { + type enumeration { + enum LINEAR { + description "This enum describes linear algorithm timer"; + } + enum EXPONENTIAL { + description "This enum describes exponential algorithm timer"; + } + } + description + "This type defines ISIS adaptive timer types"; + } + + typedef hello-padding-type { + type enumeration { + enum STRICT { + description "This enum describes strict padding"; + } + enum LOOSE { + description "This enum describes loose padding"; + } + enum ADAPTIVE { + description "This enum describes adaptive padding"; + } + enum DISABLE { + description "This enum disables padding"; + } + } + description + "This type defines ISIS hello padding type"; + } + + typedef circuit-type { + type enumeration { + enum POINT_TO_POINT { + description "This enum describes a point-to-point interface"; + } + enum BROADCAST { + description "This enum describes a broadcast interface"; + } + } + description + "This type defines ISIS interface types "; + } + + typedef metric-type { + type enumeration { + enum INTERNAL { + description "This enum describes internal route type"; + } + enum EXTERNAL { + description "This enum describes external route type"; + } + } + description + "This type defines ISIS metric type"; + } + + typedef wide-metric { + type uint32 { + range "1..16777215"; + } + description + "This type defines ISIS wide metric."; + } + + typedef narrow-metric { + type uint8 { + range "1..63"; + } + description + "This type defines ISIS narrow metric."; + } + + typedef metric-style { + type enumeration { + enum NARROW_METRIC { + description + "This enum describes narrow metric style"; + reference "RFC1195"; + } + enum WIDE_METRIC { + description + "This enum describes wide metric style"; + reference "RFC5305"; + } + } + description + "This type defines ISIS metric styles"; + } + + typedef isis-interface-adj-state { + type enumeration { + enum UP { + description + "This state describes that adjacency is established."; + } + enum DOWN { + description + "This state describes that adjacency is NOT established."; + } + enum INIT { + description + "This state describes that adjacency is establishing."; + } + enum FAILED { + description + "This state describes that adjacency is failed."; + } + } + description + "This type defines the state of the interface."; + } + + typedef net { + type string { + pattern '^[a-fA-F0-9]{2}(\.[a-fA-F0-9]{4}){3,9}\.[a-fA-F0-9]{2}$'; + } + description + "This type defines OSI NET address. A NET should should be in + the form xx.yyyy.yyyy.yyyy.00 with up to 9 sets of yyyy."; + } + + typedef area-address { + type string { + pattern '^[0-9A-Fa-f]{2}\.([0-9A-Fa-f]{4}\.){0,3}$'; + } + description + "This type defines the ISIS area address."; + } + + typedef system-id { + type string { + pattern '^[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}$'; + } + description + "This type defines ISIS system id using pattern, system id looks + like : 0143.0438.AeF0"; + } + + typedef extended-circuit-id { + type uint32; + description + "This type defines interface circuit ID."; + } + + typedef lsp-id { + type string { + pattern + '^[0-9A-Fa-f]{4}\.[0-9A-Fa-f]{4}\.[0-9A-Fa-f]' + + '{4}\.[0-9][0-9]-[0-9][0-9]$'; + } + description + "This type defines ISIS LSP ID. ISIS LSP ID type should be in + the form of xxxx.xxxx.xxxx.xx-xx"; + } + typedef snpa { + type string { + length "0 .. 20"; + } + description + "This type defines Subnetwork Point of Attachment format."; + } +} diff --git a/models/yang/common/openconfig-isis.yang b/models/yang/common/openconfig-isis.yang new file mode 100644 index 0000000000..e6b0b86b9d --- /dev/null +++ b/models/yang/common/openconfig-isis.yang @@ -0,0 +1,2076 @@ +module openconfig-isis { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/openconfig-isis"; + + prefix "oc-isis"; + + // import some basic types + import ietf-inet-types { prefix "inet"; } + import ietf-yang-types { prefix "yang"; } + import openconfig-types { prefix "oc-types"; } + import openconfig-isis-types { prefix "oc-isis-types"; } + import openconfig-routing-policy { prefix "oc-rpol"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-interfaces { prefix "oc-if"; } + import openconfig-segment-routing { prefix "oc-sr"; } + // TODO(robjs): Import authentication and keychain following merge of these + // modules. + //import openconfig-authentication-types { prefix "oc-auth-types"; } + //import openconfig-keychain { prefix "oc-keychain"; } + + // Include submodules: + // IS-IS LSP is the LSDB for IS-IS. + include openconfig-isis-lsp; + // IS-IS RT is routing-related features for IS-IS + include openconfig-isis-routing; + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net "; + + description + "This module describes a YANG model for ISIS protocol configuration. + It is a limited subset of all of the configuration parameters + available in the variety of vendor implementations, hence it is + expected that it would be augmented with vendor - specific configuration + data as needed. Additional modules or submodules to handle other + aspects of ISIS configuration, including policy, routing, types, + LSDB and additional address families are also expected. This model + supports the following ISIS configuration level hierarchy: + + ISIS + +-> { global ISIS configuration} + +-> levels +-> { level config} + +-> { system-level-counters } + +-> { level link-state-database} + +-> interface +-> { interface config } + +-> { circuit-counters } + +-> { levels config } + +-> { level adjacencies }"; + + oc-ext:openconfig-version "0.4.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.4.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements."; + reference "0.4.1"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements."; + reference "0.4.1"; + } + + revision "2018-05-14" { + description + "Update LSDB model to correct Extended IS reach TLV + bug. This change is backwards incompatible due to + adding an additional level of hierarchy to support + multiple instances of the TLV."; + reference "0.4.0"; + } + + revision "2017-07-26" { + description + "Update LSDB and fix bugs."; + reference "0.3.2"; + } + + revision "2017-05-15" { + description + "Refactor LSDB."; + reference "0.3.0"; + } + + revision "2017-01-13" { + description + "Remove top-level /isis container"; + reference "0.2.1"; + } + + revision "2016-12-15" { + description + "Add segment routing to IS-IS module"; + reference "0.2.0"; + } + + revision "2016-10-18" { + description + "Initial revision of IS-IS models."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // extension statements + + // feature statements + + // identity statements + + // typedef statements + + // grouping statements + + grouping isis-global-config { + description + "This grouping defines lobal configuration options for ISIS router."; + + // multi-instance + leaf instance { + type string; + default 0; + description + "ISIS Instance."; + } + + leaf-list net { + type oc-isis-types:net; + description + "ISIS network entity title (NET). The first 8 bits are usually + 49 (private AFI), next 16 bits represent area, next 48 bits represent + system id and final 8 bits are set to 0."; + reference + "International Organization for Standardization, Information + technology - Open Systems Interconnection-Network service + Definition - ISO/ IEC 8348:2002."; + } + + leaf maximum-area-addresses { + type uint8; + default 3; + description + "Maximum areas supported."; + } + + leaf level-capability { + type oc-isis-types:level-type; + default "LEVEL_1_2"; + description + "ISIS level capability(level-1, level-2,vlevel-1-2)."; + } + + leaf max-ecmp-paths { + type uint8; + description + "ISIS max-paths count."; + } + + leaf poi-tlv { + type boolean; + default false; + description + "ISIS purge TLV. When set to true, a TLV is added to purges to record + the system ID of the IS generating the purge."; + reference "RFC6232: Purge Originator Identification TLV for IS-IS. TLV 13."; + } + + leaf iid-tlv { + type boolean; + default false; + description + "ISIS Instance Identifier TLV. When set to trues, the IID-TLV identifies + the unique instance as well as the topology/topologies to which the + PDU applies."; + reference "RFC6822: IS-IS Multi-Instance. TLV 7"; + } + + leaf fast-flooding { + type boolean; + default true; + description + "When set to true, IS will always flood the LSP that triggered an SPF + before the router actually runs the SPF computation."; + } + } + + grouping admin-config { + description + "Re-usable grouping to enable or disable a particular IS-IS feature."; + + leaf enabled { + type boolean; + default false; + description + "When set to true, the functionality within which this leaf is + defined is enabled, when set to false it is explicitly disabled."; + } + } + + grouping isis-bfd-config { + description + "This grouping defines Bidirectionl-Forwarding-Detection + configuration."; + + //There is also BFD state under adjacency + leaf bfd-tlv { + type boolean; + description + "When set to true, BFD TLV is used. This enables support for the IS-IS + BFD TLV options, which specify that a BFD session must be established + before an IS-IS adjacency can transition to the established state. + This option should be enabled on all IS-IS neighbors on a shared + interface."; + reference "RFC6213. TLV 148"; + } + reference "RFC5880: Bidirectional Forwarding Detection (BFD)."; + } + + grouping isis-authentication-check-config { + description + "This grouping defines ISIS authentication check."; + + leaf authentication-check { + type boolean; + default true; + description + "When set to true, reject all ISIS protocol PDUs that either have a mismatch + in authentication-type or authentication-key."; + } + } + + grouping isis-metric-style-config { + description + "This grouping defines ISIS metric style."; + + leaf metric-style { + type oc-isis-types:metric-style; + description + "ISIS metric style types(narrow, wide)."; + } + } + + grouping authentication-key-config { + description + "This grouping defines authentication key configuration."; + + leaf auth-password { + type oc-types:routing-password; + description + "Authentication key string."; + } + } + + grouping keychain-base-group { + description + "This grouping defines keychain configuration."; + + container keychain { + description + "This container defines keychain parameters."; + + // TODO(robjs): Import keychain parameters following merge of the auth + // models. + //uses oc-keychain:keychain-common-base; + //uses oc-keychain:tolerance-base; + //uses oc-keychain:keychain-key-base; + } + } + + grouping isis-authentication-config { + description + "This grouping defines ISIS authentication configuration."; + + // TODO(robjs): Add authentication following merge of auth modules. + //leaf auth-type { + // type oc-auth-types:auth-type; + // description + // "ISIS authentication type (key, key-chain)."; + //} + + leaf csnp-authentication { + type boolean; + default false; + description + "Enable or disable for IS-IS CSNPs."; + } + + leaf psnp-authentication { + type boolean; + default false; + description + "Enable or disable authentication for IS-IS PSNPs."; + } + + leaf lsp-authentication { + type boolean; + default false; + description + "Enable or disable authentication for IS-IS LSPs."; + } + } + + grouping isis-authentication-group { + description + "This grouping defines ISIS authentication."; + + container config { + description + "This container defines ISIS authentication configuration."; + + uses isis-authentication-config; + } + + container state { + config false; + description + "This container defines ISIS authentication state."; + + uses isis-authentication-config; + } + + container key { + description + "This container defines ISIS authentication key"; + container config { + description + "This container defines ISIS authentication key configuration."; + + uses authentication-key-group-config { + // TODO(aashaikh): Add auth-type conditions after merge of + // auth models. + // when "../auth-type = 'KEY'"; + } + } + + container state { + config false; + description + "This container defines ISIS authentication key state."; + + uses authentication-key-group-config { + // TODO(aashaikh): Add auth-type conditions after merge of + // auth models. + // when "../auth-type = 'KEY'"; + } + } + } + + uses keychain-base-group { + // TODO(aashaikh): Add auth-type conditions after merge of + // auth models. + // when "../auth-type = 'KEY_CHAIN'"; + } + } + + grouping isis-hello-authentication-config { + description + "Configuration options for IS-IS hello authentication."; + + leaf hello-authentication { + type boolean; + default false; + description + "Enabled or disable ISIS Hello authentication."; + } + + // TODO(robjs): Add hello-auth-type following merge of auth models. + //leaf hello-auth-type { + // type oc-auth-types:auth-type; + // description + // "ISIS authentication type (key, key-chain)."; + //} + } + + grouping isis-hello-authentication-group { + description + "This grouping defines ISIS hello-authentication."; + + container config { + description + "This container defines ISIS authentication configuration."; + + uses isis-hello-authentication-config; + } + + container state { + config false; + description + "This container defines ISIS authentication state."; + + uses isis-hello-authentication-config; + } + + container key { + description + "This container defines ISIS authentication key"; + + container config { + description + "This container defines ISIS authentication key configuration."; + + uses authentication-key-group-config { + // TODO(aashaikh): Add auth-type conditions after merge of + // auth models. + // when "../auth-type = 'KEY'"; + } + } + + container state { + config false; + description + "This container defines ISIS authentication key state."; + + uses authentication-key-group-config { + // TODO(aashaikh): Add auth-type conditions after merge of + // auth models. + // when "../auth-type = 'KEY'"; + } + } + } + + uses keychain-base-group { + // TODO(aashaikh): Add auth-type conditions after merge of + // auth models. + // when "../auth-type = 'KEY_CHAIN'"; + } + } + + grouping isis-ldp-igp-config { + description + "This grouping defines ISIS/LDP Synchronization configuration."; + + leaf enabled { + type boolean; + default true; + description + "When set to true, rely on IGP/LDP synchronization. IGP cost for + link is maintained at max until LDP adjacencies are established "; + reference "RFC5443: LDP IGP Synchronization."; + } + + leaf post-session-up-delay { + type uint16; + units seconds; + description + "Specifies a delay, expressed in units of seconds, + between the LDP session to the IGP neighbor being established, and + it being considered synchronized by the IGP."; + } + } + + grouping isis-te-config { + description + "This grouping defines ISIS Traffic Engineering configuration."; + + leaf ipv4-router-id { + type inet:ipv4-address-no-zone; + description + "IPv4 MPLS Traffic Engineering Router-ID."; + } + + leaf ipv6-router-id { + type inet:ipv6-address-no-zone; + description + "IPv6 MPLS Traffic Engineering Router-ID."; + } + } + + grouping isis-reference-bandwidth-config { + description + "This grouping defines ISIS Reference Bandwidth Configuration."; + + leaf reference-bandwidth { + type uint32; + description + "ISIS Reference Bandwidth value"; + } + } + + grouping isis-overload-bit-set-config { + description + "This grouping defines ISIS Overload Bit."; + + leaf set-bit { + type boolean; + default false; + description + "When set to true, IS-IS overload bit is set."; + } + + leaf set-bit-on-boot { + type boolean; + default false; + description + "When set to true, the IS-IS overload bit is set on system boot."; + } + + leaf advertise-high-metric { + type boolean; + default false; + description + "When set to true, the local IS advertises links with the highest + available metric regardless of their configured metric. The metric + value is based on the metric style - if wide metrics are utilised + the metric is advertised as 16777214, otherwise they are advertised + with a value of 63."; + } + } + + grouping isis-overload-bit-reset-config { + description + "This grouping defines ISIS Overload Bit Reset Triggers"; + + leaf reset-trigger { + type identityref { + base oc-isis-types:OVERLOAD_RESET_TRIGGER_TYPE; + } + description + "In the case that the system sets the overload bit on start, the + system should reset the bit (i.e., clear the overload bit) upon + the specified trigger."; + } + + leaf delay { + type uint16; + units seconds; + description + "If a reset trigger is specified, the system should delay resetting + the overload bit for the specified number of seconds after the + trigger occurs."; + } + } + + grouping isis-attached-bit-config { + description + "This grouping defines ISIS Attached Bit"; + + leaf ignore-bit { + type boolean; + default false; + description + "When set to true, if the attached bit is set on an incoming Level 1 + IS-IS, the local system ignores it. In this case the local system + does not set a default route to the L1L2 router advertising the PDU + with the attached bit set."; + } + + leaf suppress-bit { + type boolean; + default false; + description + "When set to true, if the local IS acts as a L1L2 router, then the + attached bit is not advertised in locally generated PDUs."; + } + } + + grouping overload-bit-group { + description + "This grouping defines ISIS Overload Bit."; + + container config { + description + "This container defines ISIS Overload Bit configuration."; + + uses isis-overload-bit-set-config; + } + + container state { + config false; + description + "This container defines state for ISIS Overload Bit."; + + uses isis-overload-bit-set-config; + } + + container reset-triggers { + description + "This container defines state for ISIS Overload Bit reset triggers"; + + list reset-trigger { + key "reset-trigger"; + + description + "This list describes ISIS Overload reset trigger reasons."; + + leaf reset-trigger { + type leafref { + path "../config/reset-trigger"; + } + description + "Reference to the reset trigger reason"; + } + + container config { + description + "This container defines ISIS Overload Bit reset trigger + configuration."; + + uses isis-overload-bit-reset-config; + } + + container state { + config false; + description + "This container defines state for ISIS Overload Bit reset + triggers."; + + uses isis-overload-bit-reset-config; + } + } + } + } + + + grouping isis-base-level-config { + description + "This grouping defines ISIS Level configuration."; + + leaf level-number { + type oc-isis-types:level-number; + description + "ISIS level number (level-1, level-2)."; + } + } + + grouping isis-interface-level-config { + description + "This grouping defines ISIS Interface Level configuration."; + + leaf level-number { + type oc-isis-types:level-number; + description + "ISIS level number(level-1, level-2)."; + } + + leaf passive { + type boolean; + default false; + description + "ISIS passive interface admin enable/disable function."; + } + + leaf priority { + type uint8 { + range "0 .. 127"; + } + description + "ISIS neighbor priority(LAN hello PDU only)."; + } + } + + grouping isis-hello-timers-config { + description + "This grouping defines ISIS hello timers configuration."; + + leaf hello-interval { + type uint32; + description + "ISIS hello-interval value."; + } + + leaf hello-multiplier { + type uint8; + description + "ISIS hello-multiplier value."; + } + } + + grouping isis-interface-config { + description + "This grouping defines ISIS interface configuration."; + + leaf interface-id { + type oc-if:interface-id; + description + "Interface for which ISIS configuration is to be applied."; + } + + leaf passive { + type boolean; + default false; + description + "When set to true, the referenced interface is a passive interface + such that it is not eligible to establish adjacencies with other + systems, but is advertised into the IS-IS topology."; + } + + leaf hello-padding { + type oc-isis-types:hello-padding-type; + description + "This leaf controls padding type for IS-IS Hello PDUs."; + } + + leaf circuit-type { + type oc-isis-types:circuit-type; + description + "ISIS circuit type (p2p, broadcast)."; + } + } + + grouping isis-adaptive-timers-state { + description + "This grouping defines ISIS adaptive timers state"; + + leaf adaptive-timer { + type oc-isis-types:adaptive-timer-type; + description + "ISIS adaptive timer types (linear, exponential)."; + } + } + + grouping isis-lsp-generation-timers-config { + description + "This grouping defines ISIS LSP Generation timers configuration"; + + leaf lsp-max-wait-interval { + type uint64; + units milliseconds; + description + "Time interval in milliseconds that specifies max interval between + two consecutive occurrences of an LSP being generated."; + } + + leaf lsp-first-wait-interval { + type uint64; + units milliseconds; + description + "Time interval in milliseconds that specifies the first LSP generation + delay."; + } + + leaf lsp-second-wait-interval { + type uint64; + units milliseconds; + description + "Time interval in milliseconds that specifies the millisecond LSP + generation delay."; + } + } + + grouping isis-lsp-timers-config { + description + "This grouping defines ISIS LSP timers configuration"; + + leaf lsp-lifetime-interval { + type uint16; + units seconds; + default 1200; + description + "Time interval in seconds that specifies how long an LSP remains in + LSDB without being refreshed."; + } + + leaf lsp-refresh-interval { + type uint16; + units seconds; + description + "Time interval in seconds that specifies how often route topology + that a device originates is transmitted in LSPs."; + } + } + + grouping isis-spf-timers-config { + description + "This grouping defines ISIS SPF timers configuration."; + + leaf spf-hold-interval { + type uint64; + units milliseconds; + default 5000; + description + "SPF Hold Down time interval in milliseconds."; + } + + leaf spf-first-interval { + type uint64; + units milliseconds; + description + "Time interval in milliseconds between the + detection of topology change and when the SPF algorithm runs."; + } + leaf spf-second-interval { + type uint64; + units milliseconds; + description + "Time interval in milliseconds between the first and second + SPF calculation."; + } + } + + grouping isis-interface-timers-config { + description + "This grouping defines ISIS interface timers configuration."; + + leaf csnp-interval { + type uint16; + units seconds; + description + "The interval, specified in seconds, at which periodic CSNP packets + should be transmitted by the local IS."; + } + + leaf lsp-pacing-interval { + type uint64; + units milliseconds; + description + "The interval interval in milliseconds between the + detection of topology change and when the SPF algorithm runs."; + } + } + + grouping isis-transport-config { + description + "This grouping defines configuration parameters relating to the + transport protocol used by the ISIS."; + + leaf lsp-mtu-size { + type uint16; + description + "The maximum size in bytes of an IS-IS Link state PDU."; + } + } + + grouping isis-graceful-restart-config { + description + "This grouping defines ISIS graceful restart configuration."; + + leaf helper-only { + type boolean; + description + "Enable or disable the IS-IS graceful restart helper function. When + this leaf is set, the local system does not utilise the IS-IS + graceful restart procedures during its own restart, but supports + retaining forwarding information during a remote speaker's restart."; + } + reference "RFC 5306: Restart Signaling for IS-IS."; + } + + // configuration context containers + grouping inter-level-propagation-policies-structural { + description + "Propagate prefixes between IS-IS levels."; + + container inter-level-propagation-policies { + description + "Policies to propagate prefixes between IS-IS levels."; + + container level1-to-level2 { + description + "Policies relating to prefixes to be propagated from + Level 1 to Level 2."; + + container config { + description + "Configuration parameters relating to the propagation + of prefixes from IS-IS Level 1 to Level 2."; + + uses inter-level-propagation-policy-config; + } + + container state { + config false; + description + "Operational state parameters relating to the + propagation of prefixes from IS-IS Level 1 to Level 2."; + + uses inter-level-propagation-policy-config; + } + + } + + container level2-to-level1 { + description + "Policies relating to prefixes to be propagated from + Level2 to Level 1."; + + container config { + description + "Configuration parameters relating to the propagation + of prefixes from IS-IS Level 2 to Level 1."; + + uses inter-level-propagation-policy-config; + } + + container state { + config false; + description + "Operational state parameters relating to the propagation + of prefixes from IS-IS Level 2 to Level 1."; + + uses inter-level-propagation-policy-config; + } + } + } + } + + grouping inter-level-propagation-policy-config { + description + "Policy governing the propagation of prefixes between levels."; + + uses oc-rpol:apply-policy-import-config; + } + + grouping authentication-key-group-config { + description + "This grouping defines ISIS authentication key configuration."; + + uses authentication-key-config; + + // TODO(robjs): Add crypto-algorithm after merge of authentication modules. + //leaf crypto-algorithm { + // type identityref { + // base oc-auth-types:CRYPTO_TYPE; + // } + // description + // "Authentication key cryptographic algorithm to be used for key encryption."; + //} + } + + grouping isis-global-base { + description + "This grouping describes ISIS Global router."; + + container config { + description + "This container defines ISIS global configuration router."; + + uses isis-authentication-check-config; + uses isis-global-config; + } + + container state { + config false; + description + "This container defines state for ISIS global router."; + + uses isis-authentication-check-config; + uses isis-global-config; + } + + container lsp-bit { + description + "This container defines ISIS LSP Operational Bits."; + + container overload-bit { + description + "This container defines Overload Bit configuration."; + uses overload-bit-group; + } + + container attached-bit { + description + "This container defines Attached Bit."; + + container config { + description + "This container defines Attached Bit configuration."; + + uses isis-attached-bit-config; + } + + container state { + config false; + description + "This container defines state for Link State PDU Bit."; + + uses isis-attached-bit-config; + } + } + } + + container reference-bandwidth { + description + "This container defines ISIS Reference Bandwidth."; + + container config { + description + "This container defines Reference Bandwidth configuration"; + uses isis-reference-bandwidth-config; + } + + container state { + config false; + description + "This container defines state for Reference Bandwidth."; + + uses isis-reference-bandwidth-config; + } + } + + container nsr { + description + "This container defines ISIS Non-Stop Routing."; + + container config { + description + "This container defines Non-Stop-Routing configuration."; + + uses admin-config; + } + + container state { + config false; + description + "This container defines state for Non-Stop-Routing"; + + uses admin-config; + } + } + + container graceful-restart { + description + "This container defines ISIS Graceful Restart."; + + container config { + description + "This container defines ISIS graceful-restart configuration."; + + uses admin-config; + uses isis-graceful-restart-config; + } + + container state { + config false; + description + "This container defines state information for ISIS graceful-restart."; + + uses admin-config; + uses isis-graceful-restart-config; + } + } + + container timers { + description + "This container defines ISIS timers."; + + container config { + description + "This container defines ISIS global timers configuration."; + + uses isis-lsp-timers-config; + } + + container state { + config false; + description + "This container defines state information for ISIS global timers."; + + uses isis-lsp-timers-config; + } + + container spf { + description + "This container defines ISIS SPF timer settings."; + + container config { + description + "This container defines ISIS SPF timers configuration."; + + uses isis-spf-timers-config; + } + + container state { + config false; + description + "This container defines state information for ISIS SPF timers."; + + uses isis-spf-timers-config; + uses isis-adaptive-timers-state; + } + } + + container lsp-generation { + description + "This container defines ISIS LSP Generation."; + + container config { + description + "This container defines ISIS LSP Generation timers + configuration."; + + uses isis-lsp-generation-timers-config; + } + + container state { + config false; + description + "This container defines state information for ISIS LSP Generation + timers."; + + uses isis-lsp-generation-timers-config; + uses isis-adaptive-timers-state; + } + } + } + + container transport { + description + "This container defines ISIS transport."; + + container config { + description + "This container defines ISIS transport related configuration."; + + uses isis-transport-config; + } + + container state { + config false; + description + "This container defines state information for ISIS transport + parameters."; + + uses isis-transport-config; + } + } + + container mpls { + description + "Configuration and operational state relating to MPLS-related + features in IS-IS"; + + container igp-ldp-sync { + description + "Configuration and operational state relating to synchronisation + between the LDP and IS-IS"; + + container config { + description + "This container defines ISIS/IGP configuration."; + + uses isis-ldp-igp-config; + } + + container state { + config false; + description + "This container defines state information for ISIS/LDP Sync."; + + uses isis-ldp-igp-config; + } + } + } + + container igp-shortcuts { + description + "This container defines IGP shortcuts configuration and state + information."; + + uses isis-shortcuts-afi-list; + } + + container afi-safi { + description + "This container defines address-family specific configuration + and state information."; + + uses isis-afi-safi-list; + } + + uses oc-sr:sr-igp-top; + } + + grouping isis-route-preference-config { + description + "This grouping defines ISIS route preference configuration"; + + leaf external-route-preference { + type uint8 { + range "1..max"; + } + description + "Administrative Distance(preference) for external ISIS routes."; + } + + leaf internal-route-preference { + type uint8 { + range "1..max"; + } + description + "Administrative Distance(preference) for internal ISIS routes."; + } + } + + grouping isis-interfaces { + description + "This grouping defines ISIS interfaces configured on local system."; + + list interface { + key "interface-id"; + + description + "This list contains ISIS interfaces."; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "Reference to interface-id"; + } + + uses isis-interface-group; + uses oc-if:interface-ref; + } + } + + grouping isis-interface-group { + description + "This grouping defines ISIS interfaces configured on local system."; + + container config { + description + "This container defines ISIS interface configuration."; + + uses admin-config; + uses isis-interface-config; + } + + container state { + config false; + description + "This container defines state information for ISIS interfaces."; + + uses admin-config; + uses isis-interface-config; + } + + container circuit-counters { + description + "This container defines state information for ISIS circuit counters."; + + uses circuit-counters-structural; + } + + container authentication { + description + "This container defines ISIS authentication."; + + uses isis-hello-authentication-group; + } + + container afi-safi { + description + "This container defines address-family specific configuration + and state information."; + + uses isis-if-global-afi-safi-list; + } + + container levels { + description + "This container defines ISIS level specific configuration and + state information."; + + uses isis-interface-levels; + } + + container timers { + description + "This container describes ISIS interface timers configuration"; + + container config { + description + "Configuration parameters relating to interface + timers for IS-IS"; + + uses isis-interface-timers-config; + } + + container state { + config false; + description + "This container defines state information for ISIS interface timers."; + + uses isis-interface-timers-config; + } + } + + container bfd { + //TODO(bogdanov): Integrate BFD model/module + description + "This container defines BFD."; + + container config { + description + "This container defines BFD configuration parameters."; + + uses isis-bfd-config; + } + + container state { + config false; + description + "This container defines BFD state information."; + + uses isis-bfd-config; + } + } + } + + grouping isis-levels { + description + "This grouping defines global ISIS Levels."; + + list level { + key "level-number"; + + description + "Configuration and operational state parameters related to a + particular level within the IS-IS protocol instance"; + + leaf level-number { + type leafref { + path "../config/level-number"; + } + description + "Reference to ISIS level-number."; + } + + uses isis-level-group; + } + } + + grouping isis-interface-levels { + description + "This grouping defines ISIS interface Levels."; + + list level { + key "level-number"; + description + "Configuration and operational state parameters related to a + particular level on an IS-IS enabled interface."; + + leaf level-number { + type leafref { + path "../config/level-number"; + } + description + "Reference to ISIS level-number."; + } + + uses isis-interface-level-group; + } + } + + grouping isis-level-group { + description + "This grouping defines ISIS level configuration and state + information."; + + container config { + description + "This container defines ISIS level based configuration."; + + uses admin-config; + uses isis-base-level-config; + uses isis-metric-style-config; + uses isis-authentication-check-config; + } + + container state { + config false; + description + "This container defines ISIS level state information."; + + uses admin-config; + uses isis-base-level-config; + uses isis-metric-style-config; + uses isis-authentication-check-config; + } + + container system-level-counters { + description + "This container defines ISIS system level counters."; + + uses system-level-counters-structural; + } + + container link-state-database { + config false; + description + "This container defines ISIS LSDB."; + + list lsp { + key "lsp-id"; + description + "This list describes LSPs in the LSDB."; + uses lsp-state; + } + } + + container traffic-engineering { + description + "This container defines ISIS TE."; + + container config { + description + "This container defines ISIS TE configuration."; + + uses admin-config; + uses isis-te-config; + } + + container state { + config false; + description + "This container defines ISIS TE state information."; + + uses admin-config; + uses isis-te-config; + } + } + + container route-preference { + description + "This container defines Administrative Distance (or preference) + assigned to ISIS routes (level1 internal, level2 internal, level1 + external, level2 external)."; + + container config { + description + "This container defines route preference configuration."; + uses isis-route-preference-config; + } + + container state { + config false; + description + "This container defines ISIS route preference state information."; + uses isis-route-preference-config; + } + } + + container authentication { + description + "This container defines ISIS authentication."; + uses isis-authentication-group; + } + + } + + grouping isis-interface-level-group { + description + "This grouping defines ISIS interface level."; + + container config { + description + "This container defines interface ISIS level configuration."; + + uses isis-interface-level-config; + uses admin-config; + } + + container state { + config false; + description + "This container defines interface ISIS level state information."; + + uses isis-interface-level-config; + uses admin-config; + } + + container packet-counters { + description + "This container defines ISIS interface packet counters."; + + uses packet-counters-structural; + } + + container adjacencies { + config false; + description + "This container defines ISIS adjacencies."; + + list adjacency { + key "system-id"; + + description + "List of the local system's IS-IS adjacencies."; + + leaf system-id { + type leafref { + path "../state/system-id"; + } + description + "Reference to the IS neighbor."; + } + + container state { + description + "Operational state relating to the IS-IS adjacency with the + remote system"; + + uses adjacency-state; + } + } + } + + container timers { + description + "This container defines ISIS timers."; + + container config { + description + "This container defines ISIS interface hello-timers configuration."; + + uses isis-hello-timers-config; + } + + container state { + config false; + description + "This container defines ISIS interface hello-timers state."; + + uses isis-hello-timers-config; + } + } + + container afi-safi { + description + "This container defines address-family specific configuration + and state information."; + + uses isis-if-afi-safi-list; + } + + container hello-authentication { + description + "This container defines ISIS authentication."; + + uses isis-hello-authentication-group; + } + } + + + grouping isis-top { + description + "This grouping define top-level ISIS model data."; + + container isis { + description + "This container defines top-level ISIS configuration and state + information."; + + container global { + description + "This container defines global ISIS configuration and state + information."; + + uses isis-global-base; + uses inter-level-propagation-policies-structural; + } + + container levels { + description + "This container defines ISIS level configuration and state + information."; + uses isis-levels; + } + + container interfaces { + description + "This container defines global ISIS interface configuration and + state information."; + uses isis-interfaces; + } + } + } + + grouping adjacency-state { + description + "This grouping defines ISIS adjacency."; + + leaf system-id { + type oc-isis-types:system-id; + description + "ISIS neighbor system-id."; + } + + leaf neighbor-ipv4-address { + type inet:ipv4-address-no-zone; + description + "ISIS Neighbor IPv4 address."; + } + + leaf neighbor-ipv6-address { + type inet:ipv6-address-no-zone; + description + "ISIS Neighbor IPv6 address."; + } + + leaf neighbor-snpa { + type oc-isis-types:snpa; + description + "ISIS neighbor SNPA."; + } + + leaf local-extended-circuit-id { + type oc-isis-types:extended-circuit-id; + description + "Local extended circuit ID."; + } + leaf neighbor-extended-circuit-id { + type oc-isis-types:extended-circuit-id; + description + "ISIS neighbor extended circuit ID."; + } + + leaf priority { + type uint8 { + range "0..127"; + } + description + "Priority of the neighboring IS(LAN Hello only)."; + } + + leaf dis-system-id { + type oc-isis-types:system-id; + description + "DIS System ID(LAN hello only)."; + } + + leaf neighbor-circuit-type { + type oc-isis-types:level-type; + description + "Received ISIS circuit type (level-1, level-2, level-1-2)."; + } + + leaf adjacency-type { + type oc-isis-types:level-type; + description + "Formed ISIS adjacency type(level-1, level-2, level-1-2)."; + } + + leaf adjacency-state { + type oc-isis-types:isis-interface-adj-state; + description + "P2P 3-way ISIS adjacency state(up, down, init, failed)."; + reference "RFC4303. TLV 240."; + } + + leaf remaining-hold-time { + type uint16; + units seconds; + description + "Holding time in seconds for adjacency. This value is based on received + hello PDUs and the elapsed time since receipt."; + } + + leaf up-time { + type yang:timestamp; + description + "Adjacency up time."; + } + + leaf multi-topology { + type boolean; + description + "When set to true, ISIS multi-topology is supported."; + reference "RFC5129. TLV 229."; + } + + leaf-list topology { + type identityref { + base oc-isis-types:AFI_SAFI_TYPE; + } + description + "ISIS topology type support(ipv4-unicast, ipv6-unicast, + ipv4-multicast, ipv6-multicast)."; + } + + leaf restart-support { + type boolean; + description + "When set to true, Graceful-restart signaling is supported."; + } + + leaf restart-suppress { + type boolean; + description + "When set to true, adjacency is not advertised. The SA bit is used by a + starting router to request that its neighbor suppress advertisement of + the adjacency to the starting router in the neighbor's LSPs."; + } + + leaf restart-status { + type boolean; + description + "When set to true, neighbor is being helped. The RR bit is used by a + (re)starting router to signal to its neighbors that a (re)start is in + progress."; + } + + leaf-list area-address { + type oc-isis-types:area-address; + description + "List of ISIS area-address(es)."; + } + + leaf-list nlpid { + type enumeration { + enum IPV4 { + description + "IPv4 Address family."; + } + enum IPV6 { + description + "IPv6 Address family."; + } + } + description + "Supported Protocol. IPv4 is defined as (0xcc) + and IPv6 - (0x8e). ISIS reference is TLV 129."; + } + + // TODO(bogdanov): update when BFD model is integrated. + //leaf ipv4-bfd-status { + //type oc-isis-types:bfd-state; + //description + // "IPv4 BFD session status."; + //} + //leaf ipv6-bfd-status { + //type oc-isis-types:bfd-state; + //description + // "IPv4 BFD session status. "; + //} + + } + + grouping packet-counters-generic-state { + description + "Operational state parameters relating to LSP packet counters."; + + leaf received { + type yang:counter32; + description + "The number of the specified type of PDU received on the interface."; + } + leaf processed { + type yang:counter32; + description + "The number of the specified type of PDU received on the interface + that have been processed by the local system."; + } + leaf dropped { + type yang:counter32; + description + "The number of the specified type of PDU received on the interface + that have been dropped."; + } + + leaf sent { + type yang:counter32; + description + "The number of the specified type of PDU that have been sent by the + local system on the interface."; + } + + leaf retransmit { + type yang:counter32; + description + "The number of the specified type of PDU that that have been + retransmitted by the local system on the interface."; + } + } + + grouping packet-counters-structural { + description + "This grouping defines ISIS packet counter state."; + + container lsp { + description + "This container defines LSP packet counters."; + + container state { + config false; + description + "This container defines LSP PDU counters."; + + uses packet-counters-generic-state; + } + } + + container iih { + description + "This container defines IIH packet counters."; + + container state { + config false; + description + "Operational counters relating to IIH PDUs"; + + uses packet-counters-generic-state; + } + } + + container ish { + description + "This container defines ISH packet counters."; + + container state { + config false; + description + "Operational state relating to ISH PDUs."; + + uses packet-counters-generic-state; + } + } + + container esh { + description + "This container defines ESH packet counters."; + container state { + config false; + description + "Operational state relating to ESH PDUs"; + + uses packet-counters-generic-state; + } + } + + container psnp { + description + "This container defines PSNP packet counters."; + + container state { + config false; + description + "Packet counters relating to PSNPs."; + + uses packet-counters-generic-state; + } + } + + container csnp { + description + "Operational state parameters relating to CSNPs."; + + container state { + config false; + description + "Packet counters relating to CSNPs."; + + uses packet-counters-generic-state; + } + } + + container unknown { + description + "Operational state parameters relating to IS-IS PDUs that are not + otherwise classified - referred to as Unknown PDUs."; + + container state { + config false; + description + "Packet counters relating to unknown PDUs."; + + uses packet-counters-generic-state; + } + } + } + + grouping system-level-counters-state { + description + "IS-IS counters that are relevant to the system IS-IS context."; + + leaf corrupted-lsps { + type yang:counter32; + description + "Number of corrupted in-memory LSPs detected. LSPs received from the + wire with a bad checksum are silently dropped and not counted. LSPs + received from the wire with parse errors are counted by lsp-errors. MIB + Entry: SysCorrLSPs."; + } + + leaf database-overloads { + type yang:counter32; + description + "Number of times the database has become + overloaded. + MIB entry: SysLSPL(Level)DbaseOloads."; + } + + leaf manual-address-drop-from-areas { + type yang:counter32; + description + "Number of times a manual address has been dropped from area. + MIB Entry: SysManAddrDropFromAreas."; + } + + leaf exceed-max-seq-nums { + type yang:counter32; + description + "The number of times the system has attempted to exceed the maximum + sequence number. MIB Entry: SysAttmptToExMaxSeqNums."; + } + leaf seq-num-skips { + type yang:counter32; + description + "Number of times a sequence number skip has occurred. MIB Entry: + SysSeqNumSkips."; + } + + leaf own-lsp-purges { + type yang:counter32; + description + "Number of times a zero-aged copy of the system's + own LSP is received from some other node. + MIB Entry: isisSysOwnLSPPurges."; + } + + leaf id-len-mismatch { + type yang:counter32; + description + "Number of times a PDU is received with a different value for ID field + length from that of the receiving system. MIB Entry: + isisSysIDFieldLenMismatches."; + } + + leaf part-changes { + type yang:counter32; + description + "The number of partition changes detected. MIB Entry: SysPartChanges."; + } + + leaf max-area-address-mismatches { + type yang:counter32; + description + "Number of times a PDU is received with a different value for + MaximumAreaAddresses from that of the receiving system. MIB Entry: + SysMaxAreaAddrMismatches."; + } + + leaf auth-fails { + type yang:counter32; + description + "The number of authentication key failures. + MIB Entry: SysAuthFails."; + } + + leaf spf-runs { + type yang:counter32; + description + "The number of times SPF was ran at this level."; + } + + leaf auth-type-fails { + type yang:counter32; + description + "The number of authentication type mismatches."; + } + + leaf lsp-errors { + type yang:counter32; + description + "The number of received LSPs with errors."; + } + } + + grouping system-level-counters-structural { + description + "This grouping defines system level counters."; + + container state { + config false; + description + "The container defines a list of system counters for the IS."; + + uses system-level-counters-state; + } + } + + grouping circuit-counters-state { + description + "Operational state parameters relating to counters specific to one + interface or circuit."; + + leaf adj-changes { + type yang:counter32; + description + "Number of times an adjacency state change has occurred on this circuit. + MIB Entry: CircAdjChanges."; + } + + leaf init-fails { + type yang:counter32; + description + "Number of times initialization of this circuit has failed. This counts + events such as PPP NCP failures. MIB Entry: CircInitFails."; + } + + leaf rejected-adj { + type yang:counter32; + description + "Number of times an adjacency has been rejected on this circuit. MIB + Entry: CircRejAdjs."; + } + + leaf id-field-len-mismatches { + type yang:counter32; + description + "Number of times an IS-IS control PDU with an ID field length different + from that for this system has been received. + MIB Entry: CircIDFieldLenMismatches."; + } + + leaf max-area-address-mismatches { + type yang:counter32; + description + "Number of times an IS-IS control PDU with a max area address field + different from that for this system has been received. MIB Entry: + CircMaxAreaAddrMismatches."; + } + + leaf auth-type-fails { + type yang:counter32; + description + "Number of times an IS-IS control PDU with an auth type field different + from that for this system has been received. MIB Entry: + CircAuthTypeFails."; + } + + leaf auth-fails { + type yang:counter32; + description + "Number of times an IS-IS control PDU with the correct auth type has + failed to pass authentication validation. MIB Entry: CircAuthFails."; + } + + leaf lan-dis-changes { + type yang:counter32; + description + "Number of times the Designated IS has changed on this circuit at this + level. If the circuit is point to point, this count is zero. MIB Entry: + CircLANDesISChanges."; + } + + leaf adj-number { + type uint32; + description + "Number of adjacencies on this circuit. + MIB Entry: CircNumAdj."; + } + } + + grouping circuit-counters-structural { + description + "This grouping defines circuit counters."; + + container state { + config false; + description + "The container defines a list of counters for IS circuit."; + + uses circuit-counters-state; + } + } +} diff --git a/models/yang/common/openconfig-lldp-types.yang b/models/yang/common/openconfig-lldp-types.yang new file mode 100644 index 0000000000..6c4a0ac172 --- /dev/null +++ b/models/yang/common/openconfig-lldp-types.yang @@ -0,0 +1,306 @@ +module openconfig-lldp-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/lldp/types"; + + prefix "oc-lldp-types"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines types related to the LLDP protocol model."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2016-05-16" { + description + "Initial public revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity LLDP_SYSTEM_CAPABILITY { + description + "Base identity for standard LLDP system capabilities. + The system capabilities field contains a bit-map of the + capabilities that define the primary function(s) of + the system. A system may advertise more than one capability."; + reference + "Table 8-4 System Capabilities, IEEE 802.1AB-2009"; + } + + identity OTHER { + base LLDP_SYSTEM_CAPABILITY; + description + "Other capability not specified; bit position 1"; + } + + identity REPEATER { + base LLDP_SYSTEM_CAPABILITY; + description + "Repeater capability; bit position 2"; + reference + "IETF RFC 2108"; + } + + identity MAC_BRIDGE { + base LLDP_SYSTEM_CAPABILITY; + description + "MAC bridge capability; bit position 3"; + reference + "IEEE Std 802.1D"; + } + + identity WLAN_ACCESS_POINT { + base LLDP_SYSTEM_CAPABILITY; + description + "WLAN access point capability; bit position 4"; + reference + "IEEE Std 802.11 MIB"; + } + + identity ROUTER { + base LLDP_SYSTEM_CAPABILITY; + description + "Router; bit position 5"; + reference + "IETF RFC 1812"; + } + + identity TELEPHONE { + base LLDP_SYSTEM_CAPABILITY; + description + "Telephone capability; bit position 6"; + reference + "IETF RFC 4293"; + } + + identity DOCSIS_CABLE_DEVICE { + base LLDP_SYSTEM_CAPABILITY; + description + "DOCSIS cable device; bit position 7"; + reference + "IETF RFC 4639 and IETF RFC 4546"; + } + + identity STATION_ONLY { + base LLDP_SYSTEM_CAPABILITY; + description + "Station only capability, for devices that implement only an + end station capability, and for which none of the other + capabilities apply; bit position 8"; + reference + "IETF RFC 4293"; + } + + identity C_VLAN { + base LLDP_SYSTEM_CAPABILITY; + description + "C-VLAN component of a VLAN Bridge; bit position 9"; + reference + "IEEE Std 802.1Q"; + } + + identity S_VLAN { + base LLDP_SYSTEM_CAPABILITY; + description + "S-VLAN component of a VLAN Bridge; bit position 10"; + reference + "IEEE Std 802.1Q"; + } + + identity TWO_PORT_MAC_RELAY { + base LLDP_SYSTEM_CAPABILITY; + description + "Two-port MAC Relay (TPMR) capability; bit position 11"; + reference + "IEEE Std 802.1Q"; + } + + identity LLDP_TLV { + description + "A base identity which describes the TLVs in LLDP"; + } + + identity CHASSIS_ID { + base LLDP_TLV; + description + "The chassis identifier of the device associated with + the transmitting LLDP agent"; + reference "IEEE Std 802.1AB"; + } + + identity PORT_ID { + base LLDP_TLV; + description + "The port identifier associated with the interface + on with the LLDP agent is transmitting"; + reference "IEEE Std 802.1AB"; + } + + identity PORT_DESCRIPTION { + base LLDP_TLV; + description + "The description of the port that is associated with + the interface on which the LLDP agent is transmitting"; + reference "IEEE Std 802.1AB"; + } + + identity SYSTEM_NAME { + base LLDP_TLV; + description + "The assigned name (sysName or hostname) of the device + which is transmitting the LLDP PDU"; + reference "IEEE Std 802.1AB"; + } + + identity SYSTEM_DESCRIPTION { + base LLDP_TLV; + description + "The description (sysDescr) of the device which is + transmitting the LLDP PDU"; + reference "IEEE Std 802.1AB"; + } + + identity SYSTEM_CAPABILITIES { + base LLDP_TLV; + description + "The primary functions of the device transmitting the + LLDP PDU and their administrative status"; + reference "IEEE Std 802.1AB"; + } + + identity MANAGEMENT_ADDRESS { + base LLDP_TLV; + description + "The address associated with the device transmitting the + LLDP PDU which can be used for higher-layer network + management"; + reference "IEEE Std 802.1AB"; + } + + // typedef statements + + typedef chassis-id-type { + type enumeration { + enum CHASSIS_COMPONENT { + description + "Chassis identifier based on the value of entPhysicalAlias + object defined in IETF RFC 2737"; + } + enum INTERFACE_ALIAS { + description + "Chassis identifier based on the value of ifAlias object + defined in IETF RFC 2863"; + } + enum PORT_COMPONENT { + description + "Chassis identifier based on the value of entPhysicalAlias + object defined in IETF RFC 2737 for a port or backplane + component"; + } + enum MAC_ADDRESS { + description + "Chassis identifier based on the value of a unicast source + address (encoded in network byte order and IEEE 802.3 + canonical bit order), of a port on the containing chassis + as defined in IEEE Std 802-2001"; + } + enum NETWORK_ADDRESS { + description + "Chassis identifier based on a network address, + associated with a particular chassis. The encoded address + is composed of two fields. The first field is a single + octet, representing the IANA AddressFamilyNumbers value + for the specific address type, and the second field is the + network address value"; + } + enum INTERFACE_NAME { + description + "Chassis identifier based on the name of the interface, + e.g., the value of ifName object defined in IETF RFC 2863"; + } + enum LOCAL { + description + "Chassis identifier based on a locally defined value"; + } + } + description + "Type definition with enumerations describing the source of + the chassis identifier"; + reference + "IEEE 802.1AB LLDP MIB"; + } + + typedef port-id-type { + type enumeration { + enum INTERFACE_ALIAS { + description + "Chassis identifier based on the value of ifAlias object + defined in IETF RFC 2863"; + } + enum PORT_COMPONENT { + description + "Port identifier based on the value of entPhysicalAlias + object defined in IETF RFC 2737 for a port component"; + } + enum MAC_ADDRESS { + description + "Port identifier based on the value of a unicast source + address (encoded in network byte order and IEEE 802.3 + canonical bit order) associated with a port"; + } + enum NETWORK_ADDRESS { + description + "Port identifier based on a network address, + associated with a particular port"; + } + enum INTERFACE_NAME { + description + "Port identifier based on the name of the interface, + e.g., the value of ifName object defined in IETF RFC 2863"; + } + enum AGENT_CIRCUIT_ID { + description + "Port identifer based on the circuit id in the DHCP + relay agent information option as defined in IETF + RFC 3046"; + } + enum LOCAL { + description + "Port identifier based on a locally defined alphanumeric + string"; + } + } + description + "Type definition with enumerations describing the basis of + the port identifier"; + reference + "IEEE 802.1AB LLDP MIB"; + } + + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-local-routing.yang b/models/yang/common/openconfig-local-routing.yang new file mode 100644 index 0000000000..25dd243a6f --- /dev/null +++ b/models/yang/common/openconfig-local-routing.yang @@ -0,0 +1,418 @@ +module openconfig-local-routing { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/local-routing"; + + prefix "oc-loc-rt"; + + // import some basic types + import openconfig-inet-types { prefix inet; } + import openconfig-policy-types { prefix oc-pt; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-interfaces { prefix oc-if; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module describes configuration and operational state data + for routes that are locally generated, i.e., not created by + dynamic routing protocols. These include static routes, locally + created aggregate routes for reducing the number of constituent + routes that must be advertised, summary routes for IGPs, etc. + + This model expresses locally generated routes as generically as + possible, avoiding configuration of protocol-specific attributes + at the time of route creation. This is primarily to avoid + assumptions about how underlying router implementations handle + route attributes in various routing table data structures they + maintain. Hence, the definition of locally generated routes + essentially creates 'bare' routes that do not have any protocol- + specific attributes. + + When protocol-specific attributes must be attached to a route + (e.g., communities on a locally defined route meant to be + advertised via BGP), the attributes should be attached via a + protocol-specific policy after importing the route into the + protocol for distribution (again via routing policy)."; + + oc-ext:openconfig-version "1.0.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "1.0.2"; + } + + revision "2017-05-15" { + description + "Update to resolve style guide non-compliance."; + reference "1.0.1"; + } + + revision "2016-05-11" { + description + "OpenConfig public release"; + reference "1.0.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity LOCAL_DEFINED_NEXT_HOP { + description + "A base identity type of local defined next-hops"; + } + + identity DROP { + base LOCAL_DEFINED_NEXT_HOP; + description + "Discard traffic for the corresponding destination"; + } + + identity LOCAL_LINK { + base LOCAL_DEFINED_NEXT_HOP; + description + "Treat traffic towards addresses within the specified + next-hop prefix as though they are connected to a local + link. When the LOCAL_LINK next-hop type is specified, + an interface must also be specified such that + the local system can determine which link to trigger + link-layer address discovery against"; + } + + // typedef statements + + typedef local-defined-next-hop { + type identityref { + base LOCAL_DEFINED_NEXT_HOP; + } + description + "Pre-defined next-hop designation for locally generated + routes"; + } + + // grouping statements + + grouping local-generic-settings { + description + "Generic options that can be set on local routes When + they are defined"; + + leaf set-tag { + type oc-pt:tag-type; + description + "Set a generic tag value on the route. This tag can be + used for filtering routes that are distributed to other + routing protocols."; + } + } + + grouping local-static-config { + description + "Configuration data for static routes."; + + leaf prefix { + type inet:ip-prefix; + description + "Destination prefix for the static route, either IPv4 or + IPv6."; + } + + uses local-generic-settings; + } + + grouping local-static-state { + description + "Operational state data for static routes"; + } + + + grouping local-static-nexthop-config { + description + "Configuration parameters related to each next-hop entry + specified for a static route"; + + leaf index { + type string; + description + "An user-specified identifier utilised to uniquely reference + the next-hop entry in the next-hop list. The value of this + index has no semantic meaning other than for referencing + the entry."; + } + + leaf next-hop { + type union { + type inet:ip-address; + type local-defined-next-hop; + } + description + "The next-hop that is to be used for the static route + - this may be specified as an IP address, an interface + or a pre-defined next-hop type - for instance, DROP or + LOCAL_LINK. When this leaf is not set, and the interface-ref + value is specified for the next-hop, then the system should + treat the prefix as though it is directly connected to the + interface."; + } + + leaf metric { + type uint32; + description + "A metric which is utilised to specify the preference of + the next-hop entry when it is injected into the RIB. The + lower the metric, the more preferable the prefix is. When + this value is not specified the metric is inherited from + the default metric utilised for static routes within the + network instance that the static routes are being + instantiated. When multiple next-hops are specified for a + static route, the metric is utilised to determine which of + the next-hops is to be installed in the RIB. When multiple + next-hops have the same metric (be it specified, or simply + the default) then these next-hops should all be installed + in the RIB"; + } + + leaf recurse { + type boolean; + default false; + description + "Determines whether the next-hop should be allowed to + be looked up recursively - i.e., via a RIB entry which has + been installed by a routing protocol, or another static route + - rather than needing to be connected directly to an + interface of the local system within the current network + instance. When the interface reference specified within the + next-hop entry is set (i.e., is not null) then forwarding is + restricted to being via the interface specified - and + recursion is hence disabled."; + } + } + + grouping local-static-nexthop-state { + description + "Operational state parameters relating to a next-hop entry + for a static route"; + } + + + grouping local-static-top { + description + "Top-level grouping for the list of static route definitions"; + + container static-routes { + description + "Enclosing container for the list of static routes"; + + list static { + key "prefix"; + description + "List of locally configured static routes"; + + leaf prefix { + type leafref { + path "../config/prefix"; + } + description + "Reference to the destination prefix list key."; + } + + container config { + description + "Configuration data for static routes"; + + uses local-static-config; + } + + container state { + + config false; + + description + "Operational state data for static routes"; + + uses local-static-config; + uses local-static-state; + } + + container next-hops { + description + "Configuration and state parameters relating to the + next-hops that are to be utilised for the static + route being specified"; + + list next-hop { + key "index"; + + description + "A list of next-hops to be utilised for the static + route being specified."; + + leaf index { + type leafref { + path "../config/index"; + } + description + "A reference to the index of the current next-hop. + The index is intended to be a user-specified value + which can be used to reference the next-hop in + question, without any other semantics being + assigned to it."; + } + + container config { + description + "Configuration parameters relating to the next-hop + entry"; + + uses local-static-nexthop-config; + } + + container state { + config false; + description + "Operational state parameters relating to the + next-hop entry"; + + uses local-static-nexthop-config; + uses local-static-nexthop-state; + } + + uses oc-if:interface-ref; + } + } + } + } + } + + grouping local-aggregate-config { + description + "Configuration data for aggregate routes"; + + leaf prefix { + type inet:ip-prefix; + description + "Aggregate prefix to be advertised"; + } + + leaf discard { + type boolean; + default false; + description + "When true, install the aggregate route with a discard + next-hop -- traffic destined to the aggregate will be + discarded with no ICMP message generated. When false, + traffic destined to an aggregate address when no + constituent routes are present will generate an ICMP + unreachable message."; + } + + uses local-generic-settings; + + } + + grouping local-aggregate-state { + description + "Operational state data for local aggregate advertisement + definitions"; + } + + grouping local-aggregate-top { + description + "Top-level grouping for local aggregates"; + + container local-aggregates { + description + "Enclosing container for locally-defined aggregate + routes"; + + list aggregate { + key "prefix"; + description + "List of aggregates"; + + leaf prefix { + type leafref { + path "../config/prefix"; + } + description + "Reference to the configured prefix for this aggregate"; + } + + container config { + description + "Configuration data for aggregate advertisements"; + + uses local-aggregate-config; + } + + container state { + + config false; + + description + "Operational state data for aggregate + advertisements"; + + uses local-aggregate-config; + uses local-aggregate-state; + } + } + } + } + + grouping local-routes-config { + description + "Configuration data for locally defined routes"; + } + + grouping local-routes-state { + description + "Operational state data for locally defined routes"; + } + + grouping local-routes-top { + description + "Top-level grouping for local routes"; + + container local-routes { + description + "Top-level container for local routes"; + + container config { + description + "Configuration data for locally defined routes"; + + uses local-routes-config; + } + + container state { + + config false; + + description + "Operational state data for locally defined routes"; + + uses local-routes-config; + uses local-routes-state; + } + + uses local-static-top; + uses local-aggregate-top; + } + } + + uses local-routes-top; + +} diff --git a/models/yang/common/openconfig-messages.yang b/models/yang/common/openconfig-messages.yang new file mode 100644 index 0000000000..894704479d --- /dev/null +++ b/models/yang/common/openconfig-messages.yang @@ -0,0 +1,221 @@ +module openconfig-messages { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/messages"; + + prefix "oc-messages"; + + // import some basic types + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-system-logging { prefix "oc-log"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + related to Syslog messages that a device may generate. + + These messages are historically obtained through the Syslog + transport, however this module allows for obtaining them through + an alternative transport, such as a Subscribe operation over an + RPC. + + This module does not usurp traditional syslog servers, which may + still be configured through the + /yang/system/openconfig-system.yang model, rather it provies the + Operator with an alternative method of consuming messages."; + + oc-ext:openconfig-version "0.0.1"; + + revision "2018-08-13" { + description + "Initial draft."; + reference "0.0.1"; + } + + // identity statements + + identity DEBUG_SERVICE { + description + "Base identity for debug services. Identities within this base + identity are to be augmented in by vendors."; + } + + // grouping statements + + grouping messages-config { + description + "Configuration data for defining Syslog message severity."; + + leaf severity { + type oc-log:syslog-severity; + description + "Specifies that only messages of the given severity (or + greater severity) are sent over the RPC. + + This is analogous to differentiating which severity is to be + sent to legacy Syslog servers, as opposed to local buffer or + files."; + } + } + + grouping messages-state { + description + "Operational state data for Syslog messages."; + + container message { + oc-ext:telemetry-atomic; + config false; + description + "Syslog messages the client is Subscribing to. This is all + messages currently configured to be sent according to + syslog-severity."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + + // Decide if it is OK to include ALL in this leaf. + leaf msg { + type string; + description + "Message payload. If other leafs within this container not + supported, this leaf MAY include the entire message, + inclding pri, procid, app-name etc.."; + } + + leaf priority { + type uint8; + description + "The Priority value (PRIVAL) represents both the + Facility and Severity."; + reference + "IETF RFC 5424, Section 6.2.1"; + } + + leaf app-name { + type string; + description + "The APP-NAME field SHOULD identify the device or + application that originated the message."; + reference + "IETF RFC 5424, Section 6.2.5."; + } + + leaf procid { + type string; + description + "PROCID is a value that is included in the message, having + no interoperable meaning, except that a change in the value + indicates there has been a discontinuity in syslog + reporting."; + reference + "IETF RFC 5424, Section 6.2.6."; + } + + leaf msgid { + type string; + description + "The MSGID SHOULD identify the type of message. For + example, a firewall might use the MSGID 'TCPIN' for + incoming TCP traffic and the MSGID 'TCPOUT' for outgoing + TCP traffic."; + reference + "IETF RFC 5424, Section 6.2.7."; + } + } + } + + grouping debug-messages-config { + description + "Configuration data for enabling debug messages."; + + leaf service { + type identityref { + base DEBUG_SERVICE; + } + description + "Enumeration of all services which can have debugging enabled. + Vendors are to augment this base identity with their platform + or OS specific debug options."; + } + + leaf enabled { + type boolean; + default false; + description + "Enable and disable debugging."; + } + } + + grouping debug-messages-top { + description + "Configuration data for enabling Syslog debug messages."; + + container debug-entries { + description + "Enclosing container for list of debugs to enable."; + + list debug-service { + key "service"; + description + "List of debugging entries."; + + leaf service { + type leafref { + path "../config/service"; + } + description + "Reference to the debug-enable service key."; + } + + container config { + description + "Configuration data for debug service entries."; + + uses debug-messages-config; + } + + container state { + config false; + description + "Operational state data for enabled debugs."; + uses debug-messages-config; + } + } + } + } + + grouping messages-top { + description + "Top-level grouping for Syslog messages."; + + container messages { + description + "Top-level container for Syslog messages."; + + container config { + description + "Configuration data for Syslog messages."; + + uses messages-config; + } + + container state { + config false; + description + "Operational state data for a Syslog messages."; + + uses messages-config; + uses messages-state; + } + uses debug-messages-top; + } + } + uses messages-top; +} diff --git a/models/yang/common/openconfig-mpls-igp.yang b/models/yang/common/openconfig-mpls-igp.yang new file mode 100644 index 0000000000..9409138a53 --- /dev/null +++ b/models/yang/common/openconfig-mpls-igp.yang @@ -0,0 +1,131 @@ +submodule openconfig-mpls-igp { + + yang-version "1"; + + belongs-to "openconfig-mpls" { + prefix "oc-mpls"; + } + + // import some basic types + import openconfig-mpls-ldp { prefix oc-ldp; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Configuration generic configuration parameters for IGP-congruent + LSPs"; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // grouping statements + + grouping igp-lsp-common { + description + "common definitions for IGP-congruent LSPs"; + + } + + + grouping igp-lsp-setup { + description + "signaling protocol definitions for IGP-based LSPs"; + + container path-setup-protocol { + description + "select and configure the signaling method for + the LSP"; + + // uses path-setup-common; + uses oc-ldp:igp-lsp-ldp-setup; + } + } + + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-mpls-ldp.yang b/models/yang/common/openconfig-mpls-ldp.yang new file mode 100644 index 0000000000..d3f5cfda8b --- /dev/null +++ b/models/yang/common/openconfig-mpls-ldp.yang @@ -0,0 +1,884 @@ +module openconfig-mpls-ldp { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/ldp"; + + prefix "oc-ldp"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + import openconfig-inet-types { prefix oc-inet; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-types { prefix oc-types; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Configuration of Label Distribution Protocol global and LSP- + specific parameters for IGP-congruent LSPs. + + This model reuses data items defined in the IETF YANG model for + LDP described by draft-ietf-mpls-ldp-yang-04, YANG Data Model for + MPLS LDP, following an alternate structure. + + Portions of this code were derived from draft-ietf-mpls-ldp-yang-04. + Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedef statements + + typedef mpls-ldp-adjacency-type { + type enumeration { + enum LINK { + description + "Link LDP adjacency"; + } + enum TARGETED { + description + "Targeted LDP adjacency"; + } + } + description + "enumerated type for specifying LDP adjacencies"; + } + + typedef mpls-ldp-afi { + type enumeration { + enum IPV4 { + description + "IPv4 AFI for LDP adjancencies"; + } + enum IPV6 { + description + "IPv6 AFI for LDP adjancencies"; + } + } + description + "enumerated type for specifying LDP AFIs"; + } + + // grouping statements + + grouping ldp-global { + description + "Global LDP signaling configuration"; + + container ldp { + description + "LDP global signaling configuration"; + + container global { + description + "Platform wide LDP configuration and state"; + + uses mpls-ldp-global; + uses mpls-ldp-graceful-restart; + uses mpls-ldp-authentication-top; + } + + uses mpls-ldp-interface-attributes-top; + uses mpls-ldp-targeted-top; + uses mpls-ldp-neighbors-top; + + } + } + + grouping mpls-ldp-authentication-top { + description + "Grouping containing LDP authentication attributes"; + + container authentication { + description + "Global LDP authentication"; + + container config { + description + "Configuration of LDP authentication attributes"; + uses mpls-ldp-authentication-config; + } + + container state { + config false; + description + "LDP authentication state."; + uses mpls-ldp-authentication-config; + } + } + } + + grouping mpls-ldp-neighbors-top { + description + "Global LDP neighbor attributes"; + + container neighbors { + description + "State and configuration LDP neighbors attributes"; + + list neighbor { + key "lsr-id label-space-id"; + + description + "List of LDP neighbors and their attributes."; + + leaf lsr-id { + type leafref { + path "../config/lsr-id"; + } + description + "Neighbor label switch router identifier."; + } + + leaf label-space-id { + type leafref { + path "../config/label-space-id"; + } + description + "Label space ID of the neighbor."; + } + + container config { + description + "Neighbor configuration attributes."; + uses mpls-ldp-neighbor-config; + } + + container state { + config false; + description + "Neighbor state attributes."; + uses mpls-ldp-neighbor-config; + } + + container hello-adjacencies { + config false; + description "Top container for hello adjacencies + for a given LDP neighbor."; + + list hello-adjacency { + key "remote-address local-address"; + config false; + description + "List of hello adjacencies for a given LDP + neighbor."; + + leaf remote-address { + config false; + description + "Within the LDP adjacency, this attribute + shows the neighbor address."; + type leafref { + path "../state/remote-address"; + } + } + + leaf local-address { + config false; + description + "Within the LDP adjacency, this attribute + shows the local address."; + type leafref { + path "../state/local-address"; + } + } + + container state { + description + "State information for a particular LDP + hello adjacency."; + uses mpls-ldp-adjacency-state; + } + + uses oc-if:interface-ref-state; + + container hello-holdtime { + + description + "Specifies the time the sending LSR will + maintain its record of Hellos from the + receiving LSR"; + + container state { + description + "State attributes related to the + hello-holdtime."; + config false; + uses mpls-ldp-hello-holdtime-state; + } + } + + } + } + + uses mpls-ldp-authentication-top; + + } + } + } + + grouping mpls-ldp-neighbor-config { + description + "Global configuration for LDP neighbors."; + + leaf lsr-id { + type oc-inet:ip-address; + description + "Neighbor label switch router identifier."; + } + + leaf label-space-id { + type uint16; + description + "Label space ID of the neighbor."; + } + + } + + grouping mpls-ldp-adjacency-state { + + description + "Set of LDP neighbor related state attributes."; + + leaf remote-address { + description + "Within the LDP adjacency, this attribute + shows the neighbor address."; + type oc-inet:ip-address; + } + + leaf local-address { + description + "Within the LDP adjacency, this attribute + shows the local address."; + type oc-inet:ip-address; + } + + leaf adjacency-type { + description + "This attributes defines if the LDP + adjacency is from a direct link or from + targeted discovery."; + type oc-ldp:mpls-ldp-adjacency-type; + } + + leaf last-clear { + type oc-types:timeticks64; + description + "Timestamp of the last time the interface counters + were cleared. The value is the timestamp in + nanoseconds relative to the Unix Epoch (Jan 1, + 1970 00:00:00 UTC)."; + } + + leaf hello-received { + type oc-yang:counter64; + description + "Number of Hello messaged received by the device"; + } + + leaf hello-dropped { + type oc-yang:counter64; + description + "Number of Hello messaged dropped by the device"; + } + + } + + grouping mpls-ldp-hello-holdtime-state { + description + "Grouping containing the state attributes + for hello holdtime."; + + leaf adjacent { + description + "Hello holdtime attribute learned from the + LDP neighbor"; + type uint16; + } + + leaf negotiated { + description + "Hello holdtime attribute negotiated between + the LDP neighbor and the local router."; + type uint16; + } + + leaf hello-expiration { + description + "Expiration time for the hello holdtime."; + type oc-types:timeticks64; + } + + leaf next-hello { + description + "Time when the next LDP hello will be sent to + the adjacent neighbor."; + type oc-types:timeticks64; + } + + } + + grouping mpls-ldp-global { + description + "Global LDP attributes"; + + container config { + description + "Global LDP configuration attributes."; + uses mpls-ldp-global-config; + } + + container state { + config false; + description + "Global LDP state information."; + uses mpls-ldp-global-config; + } + } + + grouping mpls-ldp-global-config { + description + "Grouping containing platform wide LDP information"; + + leaf lsr-id { + type oc-inet:ip-address; + description + "Global label switch router identifier + configuration."; + reference "RFC5036 LDP Specification"; + } + + } + + grouping mpls-ldp-interface-attributes-top { + description + "Top-level structure grouping for interface + attributes"; + + container interface-attributes { + description + "Container including attributes for LDP-enabled + interfaces"; + + container config { + description + "Configuration of per-interface LDP parameters"; + uses mpls-ldp-hello-timers-top-config; + } + + container state { + config false; + description + "Per-interface LDP protocol and state information"; + uses mpls-ldp-hello-timers-top-config; + } + + container interfaces { + description + "Container aggregating all interfaces and their + LDP-specific attributes."; + + list interface { + key "interface-id"; + description + "list of per-interface LDP configurations"; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "reference to the interface-id data"; + } + + container config { + description + "Configuration of per-interface LDP parameters"; + uses mpls-ldp-interfaces-config; + uses mpls-ldp-hello-timers-top-config; + } + + container state { + config false; + description + "Per-interface LDP protocol and state information"; + + uses mpls-ldp-interfaces-config; + uses mpls-ldp-hello-timers-top-config; + + container counters { + config false; + description + "Interface specific LDP statistics and counters"; + } + } + + uses oc-if:interface-ref; + uses mpls-ldp-address-families-ldp-top; + + } + } + } + } + + grouping mpls-ldp-address-families-ldp-top { + description + "Grouping containing the state and configuration + attributes for adress families."; + + container address-families { + description + "Top container comprising the adress families + attributes"; + list address-family { + key "afi-name"; + description + "List for attributes related to address-families for LDP."; + + leaf afi-name { + type leafref { + path "../config/afi-name"; + } + description + "Adress-family name atttibute (IPv4, IPv6)."; + } + + container config { + description + "Configuration attributes related to address-families + for LDP."; + uses mpls-ldp-address-family-config; + uses admin-config; + } + + container state { + description + "State attributes related to address-families for LDP."; + config false; + uses mpls-ldp-address-family-config; + uses admin-config; + } + } + } + } + + grouping mpls-ldp-hello-timers-top-config { + + description + "Grouping containing interface-related attributes + that can be configured for LDP."; + + leaf hello-holdtime { + type uint16; + description + "Defines the time for which a neighbor adjacency will + be kept by the router while it waits for a new link + Hello message."; + reference "RFC5036 LDP Specification"; + } + + leaf hello-interval { + type uint16; + description + "Defines the interval for sending Hello messages on + each link LDP adjacency."; + } + + } + + grouping mpls-ldp-targeted-top { + + description + "Grouping containing attributes for targeted LDP"; + + container targeted { + description + "Top container for targeted LDP state and configuration + attributes."; + + container config { + description + "Configuration attributes related to targeted LDP."; + uses mpls-ldp-targeted-attributes-top-config; + } + + container state { + config false; + description + "State attributes related to targeted LDP."; + uses mpls-ldp-targeted-attributes-top-config; + } + + uses mpls-ldp-address-targeted-ldp-top; + } + } + + grouping mpls-ldp-address-targeted-ldp-top { + description + "Grouping containing address attributes for targeted LDP."; + + container address-families { + description + "Global container for IPv4 and IPv6 attributes for LDP."; + + list address-family { + key "afi-name"; + description + "List of address families for targeted LDP + configuration"; + + leaf afi-name { + type leafref { + path "../config/afi-name"; + } + description + "Adress-family name atttibute (IPv4, IPv6)."; + } + + container config { + description + "Address-family configuration for targeted LDP"; + uses mpls-ldp-address-family-config; + } + + container state { + config false; + description + "Address-family state for targeted LDP"; + uses mpls-ldp-address-family-config; + } + + container targets { + description + "Container aggregating all targeted sessions and + their LDP-specific attributes."; + + list target { + key "remote-address"; + + description + "List of LDP targets configuration"; + + leaf remote-address { + type leafref { + path "../config/remote-address"; + } + description + "Neighbor address of the targeted LDP session"; + } + + container config { + + description + "Configuration parameters of a targeted LDP + adjacency"; + + leaf remote-address { + type oc-inet:ip-address; + description + "Configuration of neighbor address of the + targeted LDP adjacency"; + } + + leaf local-address { + type oc-inet:ip-address; + description + "Local IP address of the LDP adjacency"; + } + + uses admin-config; + uses mpls-ldp-hello-timers-top-config; + } + + container state { + config false; + description + "State attributes of a targeted LDP adjacency"; + + leaf remote-address { + config false; + type oc-inet:ip-address; + description + "Neighbor address of the targeted LDP adjacency"; + } + + leaf local-address { + config false; + type oc-inet:ip-address; + description + "Local IP address of the LDP adjacency"; + } + + uses admin-config; + uses mpls-ldp-hello-timers-top-config; + } + } + } + } + } + } + + grouping mpls-ldp-address-family-config { + description + "Grouping containing adress-family name atttibute"; + + leaf afi-name { + description + "Adress-family name atttibute (IPv4, IPv6)."; + type oc-ldp:mpls-ldp-afi; + } + + } + + grouping mpls-ldp-targeted-attributes-top-config { + + description + "Grouping containing targeted LDP configuration + attributes."; + + uses mpls-ldp-hello-timers-top-config; + + leaf hello-accept { + type boolean; + description + "Enables or disables the acceptance of targeted LDP + hello messages."; + reference "RFC5036 LDP Specification"; + } + + } + + grouping mpls-ldp-interfaces-config { + description + "LDP configuration information relevant to an interface"; + + leaf interface-id { + type oc-if:interface-id; + description + "Identifier for the interface"; + } + } + + grouping mpls-ldp-graceful-restart { + description + "Attributes relating to LDP Graceful-Restart"; + + container graceful-restart { + description + "Top container for LDP graceful-restart attributes"; + + container config { + description + "LDP graceful-restart configuration attributes."; + uses mpls-ldp-graceful-restart-config; + } + + container state { + config false; + description + "LDP graceful-restart state attributes."; + uses mpls-ldp-graceful-restart-config; + } + } + } + + grouping mpls-ldp-graceful-restart-config { + description + "Configuration parameters relating to LDP Graceful-Restart"; + + uses admin-config; + + leaf reconnect-time { + type uint16; + description + "Interval for which the remote LDP peers + will wait for the local node to reconnect after a + failure"; + reference "RFC3478 Graceful Restart Mechanism for Label + Distribution Protocol"; + } + + leaf recovery-time { + type uint16; + description + "Interval used to specify the time for the remote + peer to maintain the MPLS forwarding state after + the local node has succesfully reconnected"; + reference "RFC3478 Graceful Restart Mechanism for Label + Distribution Protocol"; + } + + leaf forwarding-holdtime { + type uint16; + description + "Time that defines the interval for keeping the + node in recovery mode."; + reference "RFC3478 Graceful Restart Mechanism for Label + Distribution Protocol"; + } + + leaf helper-enable { + type boolean; + description + "Enables the graceful restart helper for LDP."; + } + } + + grouping igp-tunnel-ldp { + description + "common defintiions for LDP-signaled LSP tunnel + types"; + } + + grouping igp-lsp-ldp-setup { + description + "grouping for LDP setup attributes"; + + container ldp { + description + "LDP signaling setup for IGP-congruent LSPs"; + uses igp-tunnel-ldp; + } + } + + grouping mpls-ldp-authentication-config { + description + "LDP authentication parameters container."; + + leaf enable { + type boolean; + default false; + description + "Enables LDP authentication on the node."; + } + + leaf authentication-key { + type oc-types:routing-password; + description + "authenticate LDP signaling + messages"; + reference + "RFC1321 The MD5 Message-Digest Algorithm + RFC5036 LDP Specification"; + } + } + + grouping admin-config { + description + "Re-usable grouping to enable or disable a particular LDP feature."; + + leaf enabled { + type boolean; + default false; + description + "When set to true, the functionality within which this leaf is + defined is enabled, when set to false it is explicitly disabled."; + } + } + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-mpls-rsvp.yang b/models/yang/common/openconfig-mpls-rsvp.yang new file mode 100644 index 0000000000..d0f7f7b747 --- /dev/null +++ b/models/yang/common/openconfig-mpls-rsvp.yang @@ -0,0 +1,1457 @@ +module openconfig-mpls-rsvp { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/rsvp"; + + prefix "oc-rsvp"; + + // import some basic types + import openconfig-inet-types { prefix inet; } + import openconfig-mpls-types { prefix oc-mplst; } + import openconfig-yang-types { prefix yang; } + import openconfig-types { prefix oc-types; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-interfaces { prefix oc-if; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Configuration for RSVP-TE signaling, including global protocol + parameters and LSP-specific configuration for constrained-path + LSPs"; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + grouping mpls-rsvp-soft-preemption-config { + description + "Configuration for MPLS soft preemption"; + leaf enable { + type boolean; + default false; + description + "Enables soft preemption on a node."; + } + + leaf soft-preemption-timeout { + type uint16 { + range 0..max; + } + default 30; + description + "Timeout value for soft preemption to revert + to hard preemption. The default timeout for + soft-preemption is 30 seconds - after which + the local system reverts to hard pre-emption."; + reference "RFC5712 MPLS-TE soft preemption"; + } + } + + grouping mpls-rsvp-soft-preemption { + description + "Top level group for MPLS soft preemption"; + container soft-preemption { + description + "Protocol options relating to RSVP + soft preemption"; + container config { + description + "Configuration parameters relating to RSVP + soft preemption support"; + uses mpls-rsvp-soft-preemption-config; + } + container state { + config false; + description + "State parameters relating to RSVP + soft preemption support"; + uses mpls-rsvp-soft-preemption-config; + } + } + } + + grouping mpls-rsvp-hellos-config { + description + "RSVP protocol options configuration."; + + leaf hello-interval { + type uint16 { + range 1000..60000; + } + units milliseconds; + default 9000; + description + "set the interval in ms between RSVP hello + messages"; + reference + "RFC 3209: RSVP-TE: Extensions to RSVP for + LSP Tunnels. + RFC 5495: Description of the Resource + Reservation Protocol - Traffic-Engineered + (RSVP-TE) Graceful Restart Procedures"; + } + + leaf refresh-reduction { + type boolean; + default true; + description + "enables all RSVP refresh reduction message + bundling, RSVP message ID, reliable message delivery + and summary refresh"; + reference + "RFC 2961 RSVP Refresh Overhead Reduction + Extensions"; + } + } + + grouping mpls-rsvp-hellos { + description + "Top level grouping for RSVP hellos parameters"; + // TODO: confirm that the described semantics are supported + // on various implementations. Finer grain configuration + // will be vendor-specific + + container hellos { + description + "Top level container for RSVP hello parameters"; + + container config { + description + "Configuration parameters relating to RSVP + hellos"; + uses mpls-rsvp-hellos-config; + } + container state { + config false; + description + "State information associated with RSVP hellos"; + uses mpls-rsvp-hellos-config; + } + } + } + + grouping mpls-rsvp-subscription-config { + description + "RSVP subscription configuration"; + + leaf subscription { + type oc-types:percentage; + description + "percentage of the interface bandwidth that + RSVP can reserve"; + } + } + + grouping mpls-rsvp-subscription-state { + description + "Operational state parameters relating to the + bandwidth subscription on an interface"; + + leaf calculated-absolute-subscription-bw { + type uint64; + units "kbps"; + description + "The calculated absolute value of the bandwidth + which is reservable to RSVP-TE on the interface + prior to any adjustments that may be made from + external sources."; + } + } + + grouping mpls-rsvp-subscription { + description + "Top level group for RSVP subscription options"; + + container subscription { + description + "Bandwidth percentage reservable by RSVP + on an interface"; + + container config { + description + "Configuration parameters relating to RSVP + subscription options"; + uses mpls-rsvp-subscription-config; + } + + container state { + config false; + description + "State parameters relating to RSVP + subscription options"; + uses mpls-rsvp-subscription-config; + uses mpls-rsvp-subscription-state; + } + } + } + + grouping mpls-rsvp-graceful-restart-config { + description + "Configuration parameters relating to RSVP Graceful-Restart"; + + leaf enable { + type boolean; + default false; + description + "Enables graceful restart on the node."; + } + + leaf restart-time { + type uint32; + description + "Graceful restart time (seconds)."; + reference + "RFC 5495: Description of the Resource + Reservation Protocol - Traffic-Engineered + (RSVP-TE) Graceful Restart Procedures"; + } + leaf recovery-time { + type uint32; + description + "RSVP state recovery time"; + } + } + + grouping mpls-rsvp-graceful-restart { + description + "Top level group for RSVP graceful-restart + parameters"; + + container graceful-restart { + description + "Operational state and configuration parameters relating to + graceful-restart for RSVP"; + + container config { + description + "Configuration parameters relating to + graceful-restart"; + uses mpls-rsvp-graceful-restart-config; + } + + container state { + config false; + description + "State information associated with + RSVP graceful-restart"; + uses mpls-rsvp-graceful-restart-config; + } + } + } + + grouping mpls-rsvp-authentication-config { + description + "RSVP authentication parameters container."; + + leaf enable { + type boolean; + default false; + description + "Enables RSVP authentication on the node."; + } + + leaf authentication-key { + type string { + // Juniper supports 1..16 while + // Cisco has a much bigger range, up to 60. + length "1..32"; + } + description + "authenticate RSVP signaling + messages"; + reference + "RFC 2747: RSVP Cryptographic Authentication"; + } + } + + grouping mpls-rsvp-authentication { + description + "Top level group for RSVP authentication, + as per RFC2747"; + + container authentication { + description + "Configuration and state parameters relating to RSVP + authentication as per RFC2747"; + + container config { + description + "Configuration parameters relating + to authentication"; + uses mpls-rsvp-authentication-config; + } + + container state { + config false; + description + "State information associated + with authentication"; + uses mpls-rsvp-authentication-config; + } + } + } + + grouping mpls-rsvp-protection-config { + description + "RSVP facility (link/node) protection configuration"; + + leaf link-protection-style-requested { + type identityref { + base oc-mplst:PROTECTION_TYPE; + } + default oc-mplst:LINK_NODE_PROTECTION_REQUESTED; + description + "Style of mpls frr protection desired: + link, link-node, or unprotected"; + } + + leaf bypass-optimize-interval { + type uint16; + units seconds; + description + "interval between periodic optimization + of the bypass LSPs"; + // note: this is interface specific on juniper + // on iox, this is global. need to resolve. + } + // to be completed, things like enabling link protection, + // optimization times, etc. + } + + grouping mpls-rsvp-link-protection { + description + "Top level group for RSVP protection"; + container protection { + description + "link-protection (NHOP) related configuration"; + + container config { + description + "Configuration for link-protection"; + uses mpls-rsvp-protection-config; + } + + container state { + config false; + description + "State for link-protection"; + uses mpls-rsvp-protection-config; + } + } + } + + grouping mpls-rsvp-statistics { + description + "Top level grouping for RSVP protocol state"; + + uses mpls-rsvp-protocol-state; + } + + grouping rsvp-global { + description + "Global RSVP protocol configuration"; + container rsvp-te { + description + "RSVP-TE global signaling protocol configuration"; + + uses mpls-rsvp-session-state; + + container neighbors { + description + "Configuration and state for RSVP neighbors connecting + to the device"; + + list neighbor { + key "address"; + + config false; + + description + "List of RSVP neighbors of the local system"; + + leaf address { + type leafref { + path "../state/address"; + } + description + "Reference to the address of the RSVP neighbor"; + } + + container state { + config false; + description + "Operational state parameters relating to the + RSVP neighbor"; + uses mpls-rsvp-neighbor-state; + } + } + } + + container global { + description + "Platform wide RSVP configuration and state"; + uses mpls-rsvp-graceful-restart; + uses mpls-rsvp-soft-preemption; + uses mpls-rsvp-hellos; + + container state { + config false; + description + "Platform wide RSVP state, including counters"; + // TODO - reconcile global and per-interface + // protocol-related statistics + + container counters { + config false; + description + "Platform wide RSVP statistics and counters"; + uses mpls-rsvp-global-protocol-state; + uses mpls-rsvp-statistics; + + container errors { + description + "Error counters associated with the global RSVP-TE + instance."; + uses mpls-rsvp-error-counters; + } + } + } + } + + container interface-attributes { + description + "Attributes relating to RSVP-TE enabled interfaces"; + + list interface { + key "interface-id"; + description + "list of per-interface RSVP configurations"; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "reference to the interface-id data"; + } + + + container config { + description + "Configuration of per-interface RSVP parameters"; + uses mpls-rsvp-interfaces-config; + } + + container state { + config false; + description + "Per-interface RSVP protocol and state information"; + + uses mpls-rsvp-interfaces-state; + uses mpls-rsvp-interfaces-config; + + container counters { + config false; + description + "Interface specific RSVP statistics and counters"; + uses mpls-rsvp-protocol-state; + uses mpls-rsvp-rate-limited-messages-state; + + container errors { + description + "Interface specific RSVP error counters"; + uses mpls-rsvp-error-counters; + } + } + } + + uses oc-if:interface-ref; + uses mpls-rsvp-interface-reservations; + uses mpls-rsvp-hellos; + uses mpls-rsvp-authentication; + uses mpls-rsvp-subscription; + uses mpls-rsvp-link-protection; + } + } + } + } + + grouping rsvp-p2p-tunnel-attributes-config { + description + "properties of RSVP point-to-point paths"; + + leaf source { + when "../signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type inet:ip-address; + description + "RSVP-TE tunnel source address"; + } + + leaf soft-preemption { + when "../signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type boolean; + default false; + description + "Enables RSVP soft-preemption on this LSP"; + } + + uses rsvp-priorities-tunnel-config; + } + + grouping rsvp-priorities-tunnel-config { + description + "Configuration paramters related to RSVP-TE priorities for + an LSP tunnel"; + + leaf setup-priority { + when "../signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type uint8 { + range 0..7; + } + default 7; + description + "RSVP-TE preemption priority during LSP setup, lower is + higher priority; default 7 indicates that LSP will not + preempt established LSPs during setup"; + reference "RFC 3209 - RSVP-TE: Extensions to RSVP for + LSP Tunnels"; + } + + leaf hold-priority { + when "../signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type uint8 { + range 0..7; + } + default 0; + description + "preemption priority once the LSP is established, + lower is higher priority; default 0 indicates other LSPs + will not preempt the LSPs once established"; + reference "RFC 3209 - RSVP-TE: Extensions to RSVP for + LSP Tunnels"; + } + } + + grouping rsvp-priorities-path-config { + description + "Configuration paramters related to RSVP-TE priorities on + a primary/secondary path associated with an LSP."; + + leaf setup-priority { + when "../../../../../" + + "config/signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type uint8 { + range 0..7; + } + default 7; + description + "RSVP-TE preemption priority during LSP setup, lower is + higher priority; default 7 indicates that LSP will not + preempt established LSPs during setup"; + reference "RFC 3209 - RSVP-TE: Extensions to RSVP for + LSP Tunnels"; + } + + leaf hold-priority { + when "../../../../../" + + "config/signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type uint8 { + range 0..7; + } + default 0; + description + "preemption priority once the LSP is established, + lower is higher priority; default 0 indicates other LSPs + will not preempt the LSPs once established"; + reference "RFC 3209 - RSVP-TE: Extensions to RSVP for + LSP Tunnels"; + } + } + + grouping rsvp-p2p-path-attributes-config { + description + "properties of RSPP point-to-point paths"; + + uses rsvp-priorities-path-config; + + leaf retry-timer { + when "../../../../../" + + "config/signaling-protocol = 'PATH_SETUP_RSVP'" { + description + "When the signaling protocol is RSVP-TE "; + } + type uint16 { + range 1..600; + } + units seconds; + description + "sets the time between attempts to establish the + LSP"; + } + } + + grouping mpls-rsvp-neighbor-state { + description + "State information for RSVP neighbors"; + + leaf address { + type inet:ip-address; + description + "Address of RSVP neighbor"; + } + + leaf detected-interface { + type string; + description + "Interface where RSVP neighbor was detected"; + } + + leaf neighbor-status { + type enumeration { + enum UP { + description + "RSVP hello messages are detected from the neighbor"; + } + enum DOWN { + description + "RSVP neighbor not detected as up, due to a + communication failure or IGP notification + the neighbor is unavailable"; + } + } + description + "Enumuration of possible RSVP neighbor states"; + } + + leaf refresh-reduction { + type boolean; + description + "Suppport of neighbor for RSVP refresh reduction"; + reference + "RFC 2961 RSVP Refresh Overhead Reduction + Extensions"; + } + + } + + grouping mpls-rsvp-session-state { + description + "State information for RSVP TE sessions"; + + container sessions { + description + "Enclosing container for sessions"; + + list session { + key "local-index"; + config false; + + description + "List of RSVP sessions"; + + leaf local-index { + type leafref { + path "../state/local-index"; + } + description + "Reference to the local index for the RSVP + session"; + } + + uses mpls-rsvp-record-route-object-top; + uses mpls-rsvp-explicit-route-object-top; + + container state { + description + "Operational state parameters relating to the + RSVP session"; + + leaf local-index { + type uint64; + description + "The index used to identify the RSVP session + on the local network element. This index is + generated by the device and is unique only + to the local network element."; + } + + leaf source-address { + type inet:ip-address; + description + "Origin address of RSVP session"; + } + + leaf destination-address { + type inet:ip-address; + description + "Destination address of RSVP session"; + } + + leaf tunnel-id { + type uint16; + description + "The tunnel ID is an identifier used in the + RSVP session, which remains constant over + the life of the tunnel."; + reference "RFC 3209"; + } + + leaf lsp-id { + type uint16; + description + "The LSP ID distinguishes between two LSPs + originated from the same headend, and is + commonly used to distinguish RSVP sessions + during make before break operations."; + reference "RFC 3209"; + } + + leaf session-name { + type string; + description + "The signaled name of this RSVP session."; + } + + leaf status { + type enumeration { + enum UP { + description + "RSVP session is up"; + } + enum DOWN { + description + "RSVP session is down"; + } + } + description + "Enumeration of RSVP session states"; + } + + leaf type { + type identityref { + base oc-mplst:LSP_ROLE; + } + description + "The type/role of the RSVP session, signifing + the session's role on the current device, such as + a transit session vs. an ingress session."; + } + + leaf protection-requested { + type identityref { + base oc-mplst:PROTECTION_TYPE; + } + description + "The type of protection requested for the RSVP session"; + } + + leaf label-in { + type oc-mplst:mpls-label; + description + "Incoming MPLS label associated with this RSVP session"; + } + + leaf label-out { + type oc-mplst:mpls-label; + description + "Outgoing MPLS label associated with this RSVP session"; + } + + container sender-tspec { + description + "Operational state statistics relating to the SENDER_TSPEC + received for the RSVP session"; + + leaf rate { + type oc-types:ieeefloat32; + units "Bps"; + description + "The rate at which the head-end device generates traffic, + expressed in bytes per second."; + reference + "RFC2210: RSVP with INTSERV"; + } + + leaf size { + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The size of the token bucket that is used to determine + the rate at which the head-end device generates traffic, + expressed in bytes per second."; + reference + "RFC2210: RSVP with INTSERV"; + } + + leaf peak-data-rate { + type union { + type oc-types:ieeefloat32; + type enumeration { + enum INFINITY { + description + "The head-end device has no maximum data rate."; + } + } + } + units "bytes per second"; + description + "The maximum traffic generation rate that the head-end + device sends traffic at."; + reference + "RFC2210: RSVP with INTSERV"; + } + } + } + } + } + } //rsvp-session-state + + grouping mpls-rsvp-interfaces-config { + description + "RSVP configuration information relevant to an interface"; + + leaf interface-id { + type oc-if:interface-id; + description + "Identifier for the interface"; + } + } + + grouping mpls-rsvp-interfaces-state { + description + "RSVP state information relevant to an interface"; + + leaf max-link-bandwidth { + type oc-mplst:bandwidth-kbps; + description + "The maximum link bandwidth expressed in kilobits + per second. This value should be the same (other than + the units) as the value that is advertised into the + IGP traffic engineering database."; + } + } + + grouping mpls-rsvp-interface-reservations { + description + "Operational state related to interface bandwidth + reservations"; + + container bandwidth-reservations { + description + "Enclosing container for bandwidth reservation"; + list bandwidth-reservation { + key "priority"; + config false; + description + "Available and reserved bandwidth by priority on + the interface."; + + leaf priority { + type leafref { + path "../state/priority"; + } + description "Reference to the RSVP priority level"; + } + + container state { + description + "Operational state parameters relating to a + bandwidth reservation at a certain priority"; + + leaf priority { + type union { + type uint8 { + range 0..7; + } + type enumeration { + enum ALL { + description + "The ALL keyword represents the overall + state of the interface - i.e., the union + of all of the priority levels"; + } + } + } + description + "RSVP priority level for LSPs traversing the interface"; + } + + leaf available-bandwidth { + type oc-mplst:bandwidth-mbps; + description + "Bandwidth currently available with the priority level, + or for the entire interface when the priority is set to + ALL"; + } + + leaf reserved-bandwidth { + type oc-mplst:bandwidth-mbps; + description + "Bandwidth currently reserved within the priority level, + or the sum of all priority levels when the keyword is set + to ALL"; + } + + leaf active-reservations-count { + type yang:gauge64; + description + "Number of active RSVP reservations in the associated + priority, or the sum of all reservations when the priority + level is set to ALL"; + } + + leaf highwater-mark { + type oc-mplst:bandwidth-mbps; + description + "Maximum bandwidth reserved on the interface within the + priority, or across all priorities in the case that the + priority level is set to ALL"; + } + } + } + } + } + + grouping mpls-rsvp-global-protocol-state { + description + "RSVP protocol statistics which may not apply + on an interface, but are significant globally."; + + leaf path-timeouts { + type yang:counter64; + description + "The number of Path State Blocks (PSBs) that + have been timed out by the local system."; + } + + leaf reservation-timeouts { + type yang:counter64; + description + "The number of Reservation State Blocks (RSBs) that + have been timed out by the local system."; + } + + uses mpls-rsvp-rate-limited-messages-state; + } + + grouping mpls-rsvp-rate-limited-messages-state { + description + "Common grouping for rate limit messages"; + + leaf rate-limited-messages { + type yang:counter64; + description + "RSVP messages dropped due to rate limiting"; + } + } + + grouping mpls-rsvp-protocol-state { + description + "RSVP protocol statistics and message counters"; + + leaf in-path-messages { + type yang:counter64; + description + "Number of received RSVP Path messages"; + } + + leaf in-path-error-messages { + type yang:counter64; + description + "Number of received RSVP Path Error messages"; + } + + leaf in-path-tear-messages { + type yang:counter64; + description + "Number of received RSVP Path Tear messages"; + } + + leaf in-reservation-messages { + type yang:counter64; + description + "Number of received RSVP Resv messages"; + } + + leaf in-reservation-error-messages { + type yang:counter64; + description + "Number of received RSVP Resv Error messages"; + } + + leaf in-reservation-tear-messages { + type yang:counter64; + description + "Number of received RSVP Resv Tear messages"; + } + + leaf in-hello-messages { + type yang:counter64; + description + "Number of received RSVP hello messages"; + } + + leaf in-srefresh-messages { + type yang:counter64; + description + "Number of received RSVP summary refresh messages"; + } + + leaf in-ack-messages { + type yang:counter64; + description + "Number of received RSVP refresh reduction ack + messages"; + } + + leaf out-path-messages { + type yang:counter64; + description + "Number of sent RSVP PATH messages"; + } + + leaf out-path-error-messages { + type yang:counter64; + description + "Number of sent RSVP Path Error messages"; + } + + leaf out-path-tear-messages { + type yang:counter64; + description + "Number of sent RSVP Path Tear messages"; + } + + leaf out-reservation-messages { + type yang:counter64; + description + "Number of sent RSVP Resv messages"; + } + + leaf out-reservation-error-messages { + type yang:counter64; + description + "Number of sent RSVP Resv Error messages"; + } + + leaf out-reservation-tear-messages { + type yang:counter64; + description + "Number of sent RSVP Resv Tear messages"; + } + + leaf out-hello-messages { + type yang:counter64; + description + "Number of sent RSVP hello messages"; + } + + leaf out-srefresh-messages { + type yang:counter64; + description + "Number of sent RSVP summary refresh messages"; + } + + leaf out-ack-messages { + type yang:counter64; + description + "Number of sent RSVP refresh reduction ack messages"; + } + } + + grouping mpls-rsvp-record-route-object-top { + description + "Top-level structure grouping for list of record route + objects."; + + container record-route-objects { + description + "Enclosing container for MPLS RRO objects associated with the + traffic engineered tunnel."; + + list record-route-object { + key "index"; + config false; + + description + "Read-only list of record route objects associated with the + traffic engineered tunnel. Each entry in the list + may contain a hop IP address, MPLS label allocated + at the hop, and the flags associated with the entry."; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to the index of the record route object. + The index is used to indicate the ordering of hops in + the path."; + } + + container state { + config false; + + description + "Information related to RRO objects. The hop, label, and + optional flags are present for each entry in the list."; + + uses mpls-rsvp-record-route-object-state; + } + } + } + } + + grouping mpls-rsvp-record-route-object-state { + description + "Grouping to hold information relating to record route + objects relevant to a traffic engineering LSP."; + + leaf index { + type uint8; + description + "Index of object in the list. Used for ordering."; + } + + leaf address { + type inet:ip-address; + description + "IP router hop for RRO entry"; + } + + leaf reported-label { + type oc-mplst:mpls-label; + description + "Label reported for RRO hop"; + } + + leaf reported-flags { + type uint8; + description + "Subobject flags for MPLS label"; + } + } + + grouping mpls-rsvp-explicit-route-object-top { + description + "Top-level structure for explicit-route objects."; + + container explicit-route-objects { + description + "Enclosing container for MPLS ERO objects associated + with the traffic engineered tunnel."; + + list explicit-route-object { + key "index"; + + config false; + + description + "Read-only list of explicit route objects associated with the + traffic-engineered tunnel. Each entry in the list contains + a hop IP address, and the MPLS label allocated at the hop."; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to the index of the entry in the explicit route + object. The index is used to indicate the ordering of hops + in the path."; + } + + container state { + config false; + description + "Information related to the ERO index."; + uses mpls-rsvp-explicit-route-object-state; + } + } + } + } + + grouping mpls-rsvp-explicit-route-object-state { + description + "Grouping defining information related to an individual hop + of an ERO."; + + leaf index { + type uint64; + description + "Index of the entry in the ERO. Entries are ordered in + ascending order from the source to destination of the + LSP."; + } + + leaf loose { + type boolean; + description + "When set to true, indicates that the hop of the ERO is + a loose hop within the explicit route. If unset, indicates + that the hop must explicitly traverse the entity specified + in the ERO hop as the next-entity."; + } + + leaf type { + type enumeration { + enum IPV4 { + description + "The hop represents an IPv4 prefix."; + reference "RFC3209"; + } + enum IPV6 { + description + "The hop represents an IPv6 prefix."; + reference "RFC3209"; + } + enum ASN { + description + "The hop represents an autonomous system number."; + reference "RFC3209"; + } + enum ASN4 { + description + "The hop represents a 4-byte autonomous system number."; + } + enum LABEL { + description + "The hop represents an MPLS label."; + reference "RFC3473"; + } + enum UNNUMBERED_INTERFACE { + description + "The hop represents an unnumbered interface."; + reference "RFC3477"; + } + } + description + "The type of hop indicated by the ERO entry."; + } + + leaf ip-prefix { + type inet:ip-prefix; + description + "The IPv4 or IPv6 prefix indicated by the ERO. Specified + only when the ERO hop is an IPv4 or IPv6 prefix."; + } + + leaf asn { + type inet:as-number; + description + "The autonomous system number indicated by the ERO. Specified + only when the ERO hop is an 2 or 4-byte AS number."; + } + + leaf label { + type oc-mplst:mpls-label; + description + "The MPLS label specified in the ERO hop. Specified only when + the hop is an MPLS label."; + } + + leaf interface-id { + type uint32; + description + "The interface ID for an unnumbered interface. Specified only + when the ERO hop is a unnumbered interface."; + } + reference + "RFC3477 - Signalling Unnumbered Links in Resource + ReSerVation Protocol - Traffic Engineering (RSVP-TE)"; + + } + + grouping mpls-rsvp-error-counters { + description + "Grouping containing definitions of leaves relating to + errors in RSVP-TE. This grouping can be used in different + contexts - e.g., per-RSVP-TE protocol instance, or per- + interface such that the errors represented should + correspond to the number of errors that have occurred for + the context in which the grouping is used."; + + leaf authentication-fail { + type yang:counter64; + description + "The number of packets received that have failed RSVP-TE + authentication checks in the specified context."; + } + + leaf bad-checksum { + type yang:counter64; + description + "The number of packets received that have an incorrect RSVP-TE + checksum in the context."; + } + + leaf bad-packet-format { + type yang:counter64; + description + "The number of packets received that were dropped due to being + badly formed in the context."; + } + + leaf bad-packet-length { + type yang:counter64; + description + "The number of packets received that were dropped due to having + an invalid length specified in the context."; + } + + leaf out-of-order { + type yang:counter64; + description + "The number of messages received out of order in the context."; + } + + leaf received-nack { + type yang:counter64; + description + "The number of NACK RESV messages received in the context."; + } + + leaf transmit-failure { + type yang:counter64; + description + "The total number of packets dropped on transmit in the context."; + } + + leaf transmit-queue-full { + type yang:counter64; + description + "The number of packets dropped due to the transmit queue being + full in the context."; + } + + leaf unknown-ack { + type yang:counter64; + description + "The number of packets received containing an ACK for an unknown + message ID in the context."; + } + + leaf unknown-nack { + type yang:counter64; + description + "The number of packets received containing a NACK for an unknown + message ID in the context."; + } + } + + + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-mpls-sr.yang b/models/yang/common/openconfig-mpls-sr.yang new file mode 100644 index 0000000000..8fc670f985 --- /dev/null +++ b/models/yang/common/openconfig-mpls-sr.yang @@ -0,0 +1,149 @@ +module openconfig-mpls-sr { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/mpls-sr"; + + prefix "oc-mpls-sr"; + + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Configuration for MPLS with segment routing-based LSPs, + including global parameters, and LSP-specific configuration for + both constrained-path and IGP-congruent LSPs"; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping sr-path-attributes-config { + description + "Configuration parameters relating to SR-TE LSPs"; + + leaf sid-selection-mode { + type enumeration { + enum ADJ_SID_ONLY { + description + "The SR-TE tunnel should only use adjacency SIDs + to build the SID stack to be pushed for the LSP"; + } + enum MIXED_MODE { + description + "The SR-TE tunnel can use a mix of adjacency + and prefix SIDs to build the SID stack to be pushed + to the LSP"; + } + } + default MIXED_MODE; + description + "The restrictions placed on the SIDs to be selected by the + calculation method for the explicit path when it is + instantiated for a SR-TE LSP"; + } + + leaf sid-protection-required { + type boolean; + default "false"; + description + "When this value is set to true, only SIDs that are + protected are to be selected by the calculating method + when the explicit path is instantiated by a SR-TE LSP."; + } + } + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-mpls-static.yang b/models/yang/common/openconfig-mpls-static.yang new file mode 100644 index 0000000000..f3d051297d --- /dev/null +++ b/models/yang/common/openconfig-mpls-static.yang @@ -0,0 +1,318 @@ +submodule openconfig-mpls-static { + + yang-version "1"; + + belongs-to "openconfig-mpls" { + prefix "mpls"; + } + + // import some basic types + import openconfig-mpls-types {prefix oc-mplst; } + import openconfig-inet-types { prefix inet; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Defines static LSP configuration"; + + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // grouping statements + + grouping static-lsp-common-config { + description + "common definitions for static LSPs"; + + leaf next-hop { + type inet:ip-address; + description + "next hop IP address for the LSP"; + } + + leaf incoming-label { + type oc-mplst:mpls-label; + description + "label value on the incoming packet"; + } + + leaf push-label { + type oc-mplst:mpls-label; + description + "label value to push at the current hop for the + LSP"; + } + } + + grouping static-lsp-ingress-config { + description + "Configuration data for ingress LSPs"; + + uses static-lsp-common-config; + } + + grouping static-lsp-ingress-state { + description + "Operational state data for ingress LSPs"; + } + + grouping static-lsp-ingress-top { + description + "Top-level grouping for ingress LSP data"; + + container ingress { + description + "Static LSPs for which the router is an + ingress node"; + + container config { + description + "Configuration data for ingress LSPs"; + + uses static-lsp-ingress-config; + } + + container state { + + config false; + + description + "Operational state data for ingress LSPs"; + + uses static-lsp-ingress-config; + uses static-lsp-ingress-state; + } + } + } + + grouping static-lsp-transit-config { + description + "Configuration data for transit LSPs"; + + uses static-lsp-common-config; + } + + grouping static-lsp-transit-state { + description + "Operational state data for transit LSPs"; + } + + grouping static-lsp-transit-top { + description + "Top-level grouping for transit LSP data"; + + container transit { + description + "Static LSPs for which the router is an + transit node"; + + container config { + description + "Configuration data for transit LSPs"; + + uses static-lsp-transit-config; + } + + container state { + + config false; + + description + "Operational state data for transit LSPs"; + + uses static-lsp-transit-config; + uses static-lsp-transit-state; + } + } + } + + grouping static-lsp-egress-config { + description + "Configuration data for egress LSPs"; + + uses static-lsp-common-config; + } + + grouping static-lsp-egress-state { + description + "Operational state data for egress LSPs"; + } + + grouping static-lsp-egress-top { + description + "Top-level grouping for egress LSP data"; + + container egress { + description + "Static LSPs for which the router is an + egress node"; + + container config { + description + "Configuration data for egress LSPs"; + + uses static-lsp-egress-config; + } + + container state { + + config false; + + description + "Operational state data for egress LSPs"; + + uses static-lsp-egress-config; + uses static-lsp-egress-state; + } + } + } + + grouping static-lsp-config { + description + "Configuration data for static LSPs"; + + leaf name { + type string; + description + "name to identify the LSP"; + } + } + + grouping static-lsp-state { + description + "Operational state data for static LSPs"; + + } + + grouping static-lsp-top { + description + "grouping for top level list of static LSPs"; + + + list static-lsp { + key "name"; + description + "list of defined static LSPs"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference the name list key"; + } + + container config { + description + "Configuration data for the static lsp"; + + uses static-lsp-config; + } + + container state { + config false; + + description + "Operational state data for the static lsp"; + + uses static-lsp-config; + uses static-lsp-state; + + } + + // TODO: separation into ingress, transit, egress may help + // to figure out what exactly is configured, but need to + // consider whether implementations can support the + // separation + uses static-lsp-ingress-top; + uses static-lsp-transit-top; + uses static-lsp-egress-top; + } + } + + // data definition statements + + // augment statements + + +} diff --git a/models/yang/common/openconfig-mpls-te.yang b/models/yang/common/openconfig-mpls-te.yang new file mode 100644 index 0000000000..4a9953e3e6 --- /dev/null +++ b/models/yang/common/openconfig-mpls-te.yang @@ -0,0 +1,1386 @@ +submodule openconfig-mpls-te { + + yang-version "1"; + + belongs-to "openconfig-mpls" { + prefix "oc-mpls"; + } + + + // import some basic types + import openconfig-inet-types { prefix inet; } + import openconfig-mpls-rsvp { prefix oc-rsvp; } + import openconfig-mpls-sr { prefix oc-sr; } + import openconfig-mpls-types {prefix oc-mplst; } + import openconfig-types { prefix oc-types; } + import openconfig-yang-types { prefix yang; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Configuration related to constrained-path LSPs and traffic + engineering. These definitions are not specific to a particular + signaling protocol or mechanism (see related submodules for + signaling protocol-specific configuration)."; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // typedef statements + + typedef te-bandwidth-type { + type enumeration { + enum SPECIFIED { + description + "Bandwidth is explicitly specified"; + } + enum AUTO { + description + "Bandwidth is automatically computed"; + } + } + description + "enumerated type for specifying whether bandwidth is + explicitly specified or automatically computed"; + } + + typedef mpls-srlg-flooding-type { + type enumeration { + enum FLOODED_SRLG { + description + "SRLG is flooded in the IGP"; + } + enum STATIC_SRLG { + description + "SRLG is not flooded, the members are + statically configured"; + } + } + description + "Enumerated bype for specifying how the SRLG is flooded"; + } + + typedef mpls-hop-type { + type enumeration { + enum LOOSE { + description + "loose hop in an explicit path"; + } + enum STRICT { + description + "strict hop in an explicit path"; + } + } + description + "enumerated type for specifying loose or strict + paths"; + } + + typedef te-metric-type { + type union { + type enumeration { + enum IGP { + description + "set the LSP metric to track the underlying + IGP metric"; + } + } + type uint32; + } + description + "union type for setting the LSP TE metric to a + static value, or to track the IGP metric"; + } + + typedef cspf-tie-breaking { + type enumeration { + enum RANDOM { + description + "CSPF calculation selects a random path among + multiple equal-cost paths to the destination"; + } + enum LEAST_FILL { + description + "CSPF calculation selects the path with greatest + available bandwidth"; + } + enum MOST_FILL { + description + "CSPF calculation selects the path with the least + available bandwidth"; + } + } + default RANDOM; + description + "type to indicate the CSPF selection policy when + multiple equal cost paths are available"; + } + + + // grouping statements + + grouping te-tunnel-reoptimize-config { + description + "Definition for reoptimize timer configuration"; + + leaf reoptimize-timer { + type uint16; + units seconds; + description + "frequency of reoptimization of + a traffic engineered LSP"; + } + } + + grouping te-lsp-auto-bandwidth-config { + description + "Configuration parameters related to autobandwidth"; + + leaf enabled { + type boolean; + default false; + description + "enables mpls auto-bandwidth on the + lsp"; + } + + leaf min-bw { + type oc-mplst:bandwidth-kbps; + description + "set the minimum bandwidth in Kbps for an + auto-bandwidth LSP"; + } + + leaf max-bw { + type oc-mplst:bandwidth-kbps; + description + "set the maximum bandwidth in Kbps for an + auto-bandwidth LSP"; + } + + leaf adjust-interval { + type uint32; + description + "time in seconds between adjustments to + LSP bandwidth"; + } + + leaf adjust-threshold { + type oc-types:percentage; + description + "percentage difference between the LSP's + specified bandwidth and its current bandwidth + allocation -- if the difference is greater than the + specified percentage, auto-bandwidth adjustment is + triggered"; + } + } + + grouping te-lsp-auto-bandwidth-state { + description + "Operational state parameters relating to auto-bandwidth"; + + leaf interval-high-bw { + type oc-mplst:bandwidth-kbps; + description + "The maximum measured bandwidth during the current + auto-bandwidth adjust interval expressed in kilobits + per second."; + } + } + + grouping te-lsp-overflow-config { + description + "configuration for mpls lsp bandwidth + overflow adjustment"; + + leaf enabled { + type boolean; + default false; + description + "enables mpls lsp bandwidth overflow + adjustment on the lsp"; + } + + leaf overflow-threshold { + type oc-types:percentage; + description + "bandwidth percentage change to trigger + an overflow event"; + + } + + leaf trigger-event-count { + type uint16; + description + "number of consecutive overflow sample + events needed to trigger an overflow adjustment"; + } + } + + grouping te-lsp-underflow-config { + description + "configuration for mpls lsp bandwidth + underflow adjustment"; + + leaf enabled { + type boolean; + default false; + description + "enables bandwidth underflow + adjustment on the lsp"; + } + + leaf underflow-threshold { + type oc-types:percentage; + description + "bandwidth percentage change to trigger + and underflow event"; + } + + leaf trigger-event-count { + type uint16; + description + "number of consecutive underflow sample + events needed to trigger an underflow adjustment"; + } + } + + grouping te-path-placement-constraints-config { + description + "Configuration data for link affinities"; + + leaf-list exclude-group { + type leafref { + path "../../../../../../../../../../te-global-attributes" + + "/mpls-admin-groups/admin-group/admin-group-name"; + } + description + "list of references to named admin-groups to exclude in + path calculation."; + } + + leaf-list include-all-group { + type leafref { + path "../../../../../../../../../../te-global-attributes" + + "/mpls-admin-groups/admin-group/admin-group-name"; + } + description + "list of references to named admin-groups of which all must + be included"; + } + + leaf-list include-any-group { + type leafref { + path "../../../../../../../../../../te-global-attributes" + + "/mpls-admin-groups/admin-group/admin-group-name"; + } + description + "list of references to named admin-groups of which one must + be included"; + } + } + + grouping te-path-placement-constraints-state { + description + "Operational state data for link affinities"; + //TODO: currently a placeholder + } + + grouping te-path-placement-constraints-top { + description + "Top-level grouping "; + + container admin-groups { + description + "Top-level container for include/exclude constraints for + link affinities"; + + container config { + description + "Configuration data "; + + uses te-path-placement-constraints-config; + } + + container state { + config false; + + description + "Operational state data "; + + uses te-path-placement-constraints-config; + uses te-path-placement-constraints-state; + } + } + } + + grouping te-tunnel-protection-config { + description + "Configuration parameters related to LSP + protection"; + leaf protection-style-requested { + type identityref { + base oc-mplst:PROTECTION_TYPE; + } + default oc-mplst:UNPROTECTED; + description + "style of mpls frr protection desired: can be + link, link-node or unprotected."; + } + } + + grouping explicit-route-subobject-config { + description + "The explicit route subobject grouping"; + + leaf address { + type inet:ip-address; + description + "router hop for the LSP path"; + } + + leaf hop-type { + type mpls-hop-type; + description + "strict or loose hop"; + } + + leaf index { + type uint8 { + range "0..255"; + } + description + "Index of this explicit route object to express + the order of hops in the path"; + } + + } + + grouping named-explicit-path-config { + description + "Configuration parameters relating to a named + explicit path"; + + leaf name { + type string; + description + "A string name that uniquely identifies an explicit + path"; + } + } + + // Explicit paths config somewhat following the IETF model + grouping explicit-paths-top { + description + "Top level global explicit path configuration + grouping"; + + container named-explicit-paths { + description + "Enclosing container for the named explicit paths"; + list named-explicit-path { + key "name"; + description + "A list of explicit paths"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "A string name that uniquely identifies + an explicit path"; + } + + container config { + description + "Configuration parameters relating to named explicit + paths"; + uses named-explicit-path-config; + uses oc-sr:sr-path-attributes-config; + } + + container state { + config false; + description + "Operational state parameters relating to the named + explicit paths"; + uses named-explicit-path-config; + uses oc-sr:sr-path-attributes-config; + } + + container explicit-route-objects { + description + "Enclosing container for EROs"; + + list explicit-route-object { + key "index"; + description + "List of explicit route objects"; + + leaf index { + type leafref { + path "../config/index"; + } + + description + "Index of this explicit route object, + to express the order of hops in path"; + } + + container config { + description + "Configuration parameters relating to an explicit + route"; + uses explicit-route-subobject-config; + } + + + container state { + config false; + description + "State parameters relating to an explicit route"; + uses explicit-route-subobject-config; + } + } + } + } + } + } + + grouping mpls-te-srlg-config { + description + "Configuration of various attributes associated + with the SRLG"; + + leaf name { + type string; + description + "SRLG group identifier"; + } + + leaf value { + type uint32; + description + "group ID for the SRLG"; + } + + leaf cost { + type uint32; + description + "The cost of the SRLG to the computation + algorithm"; + } + + leaf flooding-type { + type mpls-srlg-flooding-type; + default FLOODED_SRLG; + description + "The type of SRLG, either flooded in the IGP or + statically configured"; + } + } + + grouping mpls-te-srlg-members-config { + description + "Configuration of the membership of the SRLG"; + + leaf from-address { + type inet:ip-address; + description + "IP address of the a-side of the SRLG link"; + } + + leaf to-address { + type inet:ip-address; + description + "IP address of the z-side of the SRLG link"; + } + } + + grouping mpls-te-srlg-top { + description + "Top level grouping for MPLS shared + risk link groups."; + + container srlgs { + description + "Shared risk link groups attributes"; + list srlg { + key "name"; + description + "List of shared risk link groups"; + + leaf name { + type leafref { + path "../config/name"; + // Requires YANG 1.1 + //require-instance true; + } + description + "The SRLG group identifier"; + } + + container config { + description + "Configuration parameters related to the SRLG"; + uses mpls-te-srlg-config; + } + + container state { + config false; + description + "State parameters related to the SRLG"; + uses mpls-te-srlg-config; + } + + container static-srlg-members { + when "../config/flooding-type = 'STATIC_SRLG'" { + description + "Include this container for static + SRLG specific configuration"; + } + description + "SRLG members for static (not flooded) SRLGs "; + + list members-list { + key "from-address"; + description + "List of SRLG members, which are expressed + as IP address endpoints of links contained in the + SRLG"; + + leaf from-address { + type leafref { + path "../config/from-address"; + // Requires YANG 1.1 + //require-instance true; + } + description + "The from address of the link in the SRLG"; + } + + container config { + description + "Configuration parameters relating to the + SRLG members"; + uses mpls-te-srlg-members-config; + } + + container state { + config false; + description + "State parameters relating to the SRLG + members"; + uses mpls-te-srlg-members-config; + } + } + } + } + } + } + + grouping te-global-tunnel-config { + description + "Configuration parameters relevant to a single + traffic engineered tunnel."; + + leaf name { + type string; + description + "The tunnel name"; + } + + leaf type { + type identityref { + base oc-mplst:TUNNEL_TYPE; + } + description + "Tunnel type, p2p or p2mp"; + } + + leaf signaling-protocol { + type identityref { + base oc-mplst:PATH_SETUP_PROTOCOL; + } + description + "Signaling protocol used to set up this tunnel"; + } + + leaf description { + type string; + description + "optional text description for the tunnel"; + } + + leaf admin-status { + type identityref { + base oc-mplst:TUNNEL_ADMIN_STATUS; + } + default oc-mplst:ADMIN_UP; + description + "TE tunnel administrative state."; + } + + leaf preference { + type uint8 { + range "1..255"; + } + description + "Specifies a preference for this tunnel. + A lower number signifies a better preference"; + } + + leaf metric-type { + type identityref { + base oc-mplst:LSP_METRIC_TYPE; + } + default oc-mplst:LSP_METRIC_INHERITED; + description + "The type of metric specification that should be used to set + the LSP(s) metric"; + } + + leaf metric { + type int32; + description + "The value of the metric that should be specified. The value + supplied in this leaf is used in conjunction with the metric + type to determine the value of the metric used by the system. + Where the metric-type is set to LSP_METRIC_ABSOLUTE - the + value of this leaf is used directly; where it is set to + LSP_METRIC_RELATIVE, the relevant (positive or negative) + offset is used to formulate the metric; where metric-type + is LSP_METRIC_INHERITED, the value of this leaf is not + utilised"; + } + + leaf shortcut-eligible { + type boolean; + default "true"; + description + "Whether this LSP is considered to be eligible for us as a + shortcut in the IGP. In the case that this leaf is set to + true, the IGP SPF calculation uses the metric specified to + determine whether traffic should be carried over this LSP"; + } + + leaf protection-style-requested { + type identityref { + base oc-mplst:PROTECTION_TYPE; + } + default oc-mplst:UNPROTECTED; + description + "style of mpls frr protection desired: can be + link, link-node or unprotected."; + } + + uses te-tunnel-reoptimize-config; + uses oc-rsvp:rsvp-p2p-tunnel-attributes-config; + + } + + grouping tunnel-p2p-attributes-config { + description + "Configuration related to p2p LSPs"; + leaf destination { + type inet:ip-address; + description + "P2P tunnel destination address"; + } + } + + grouping p2p-path-state { + description + "Operational state parameters for p2p paths"; + + leaf-list associated-rsvp-sessions { + type leafref { + path "../../../../../../../../../signaling-protocols/" + + "rsvp-te/sessions/session/local-index"; + } + description + "If the signalling protocol specified for this path is + RSVP-TE, this leaf-list provides a reference to the associated + sessions within the RSVP-TE protocol sessions list, such + that details of the signaling can be retrieved. More than + one session may exist during re-signalling such as + make-before-break."; + } + + leaf spf-metric { + type uint64; + description + "The IGP metric of the shortest path to the LSP destination. + This value is used to compare the current metric of the + constrained path to the shortest path that is available in + the network topology."; + } + + leaf cspf-metric { + type uint64; + description + "The IGP metric of the path currently used by the LSP. + This value is used to represent the metric of the path + used by the LSP following the execution of the CSPF + algorithm and signalling of the LSP."; + } + } + + grouping p2p-path-config { + description + "Configuration parameters for p2p paths"; + + leaf name { + type string; + description + "Path name"; + } + + leaf path-computation-method { + type identityref { + base oc-mplst:PATH_COMPUTATION_METHOD; + } + default oc-mplst:LOCALLY_COMPUTED; + description + "The method used for computing the path, either + locally computed, queried from a server or not + computed at all (explicitly configured)."; + } + + leaf use-cspf { + when "../path-computation-method = 'LOCALLY_COMPUTED'" { + description + "The use of cspf when the path-computation method is + local computation"; + } + type boolean; + description + "Flag to enable CSPF for locally computed LSPs"; + } + + leaf cspf-tiebreaker { + when "../path-computation-method = 'LOCALLY_COMPUTED'" { + description + "The cspf tiebreaking method when the path is + locally computed"; + } + type cspf-tie-breaking; + description + "Determine the tie-breaking method to choose between + equally desirable paths during CSFP computation"; + } + + + leaf path-computation-server { + when "../path-computation-method = 'EXTERNALLY_QUERIED'" { + description + "The path-computation server when the path is + externally queried"; + } + type inet:ip-address; + description + "Address of the external path computation + server"; + } + + leaf explicit-path-name { + when "../path-computation-method = 'EXPLICITLY_DEFINED'" { + description + "The name of the explicitly defined path used"; + } + + type leafref { + path "../../../../../../../" + + "named-explicit-paths/named-explicit-path/" + + "config/name"; + // Requires YANG 1.1 + //require-instance true; + } + description + "reference to a defined path"; + } + + leaf preference { + type uint8 { + range "1..255"; + } + description + "Specifies a preference for this path. The lower the + number higher the preference"; + } + + uses oc-rsvp:rsvp-p2p-path-attributes-config; + } + + + grouping te-tunnel-p2p-top { + description + "Top level grouping for p2p configuration"; + + container p2p-tunnel-attributes { + when "../config/type = 'P2P'" { + description + "Include this container for LSPs of type P2P"; + } + description + "Parameters related to LSPs of type P2P"; + + container config { + description + "Configuration parameters for P2P LSPs"; + uses tunnel-p2p-attributes-config; + } + + container state { + config false; + description + "State parameters for P2P LSPs"; + uses tunnel-p2p-attributes-config; + } + + uses p2p-primary-paths-top; + uses p2p-secondary-paths-top; + } + } + + + grouping te-tunnel-state { + description + "Counters and statistical data relevent to a single + tunnel."; + + leaf oper-status { + type identityref { + base oc-mplst:LSP_OPER_STATUS; + } + description + "The operational status of the TE tunnel"; + } + + leaf role { + type identityref { + base oc-mplst:LSP_ROLE; + } + description + "The lsp role at the current node, whether it is headend, + transit or tailend."; + } + + leaf auto-generated { + type boolean; + description + "If the LSP was auto-generated by the system this leaf + should be set to true. Examples of auto-generated LSPs + are dynamically created backup LSPs to meet a FRR + policy."; + } + + container counters { + description + "State data for MPLS label switched paths. This state + data is specific to a single label switched path."; + + leaf bytes { + type yang:counter64; + description + "Number of bytes that have been forwarded over the + label switched path."; + } + + leaf packets { + type yang:counter64; + description + "Number of pacets that have been forwarded over the + label switched path."; + } + + leaf path-changes { + type yang:counter64; + description + "Number of path changes for the label switched path"; + } + + leaf state-changes { + type yang:counter64; + description + "Number of state changes for the label switched path"; + } + + leaf online-time { + type oc-types:timeticks64; + description + "Indication of the time the label switched path + transitioned to an Oper Up or in-service state. + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + leaf current-path-time { + type oc-types:timeticks64; + description + "Indicates the time the LSP switched onto its + current path. The value is reset upon a LSP path + change. + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + + } + + leaf next-reoptimization-time { + type oc-types:timeticks64; + description + "Indicates the next scheduled time the LSP + will be reoptimized. + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + + } + } + } + + grouping te-tunnel-bandwidth-config { + description + "Configuration parameters related to bandwidth for a tunnel"; + + leaf specification-type { + type te-bandwidth-type; + default SPECIFIED; + description + "The method used for settign the bandwidth, either explicitly + specified or configured"; + } + + leaf set-bandwidth { + when "../specification-type = 'SPECIFIED'" { + description + "The bandwidth value when bandwidth is explicitly + specified"; + } + type oc-mplst:bandwidth-kbps; + description + "set bandwidth explicitly, e.g., using + offline calculation"; + } + } + + grouping te-tunnel-bandwidth-state { + description + "Operational state parameters relating to bandwidth for a tunnel"; + + leaf signaled-bandwidth { + type oc-mplst:bandwidth-kbps; + description + "The currently signaled bandwidth of the LSP. In the case where + the bandwidth is specified explicitly, then this will match the + value of the set-bandwidth leaf; in cases where the bandwidth is + dynamically computed by the system, the current value of the + bandwidth should be reflected."; + } + } + + grouping te-tunnel-bandwidth-top { + description + "Top level grouping for specifying bandwidth for a tunnel"; + + container bandwidth { + description + "Bandwidth configuration for TE LSPs"; + + container config { + description + "Configuration parameters related to bandwidth on TE + tunnels:"; + uses te-tunnel-bandwidth-config; + } + + container state { + config false; + description + "State parameters related to bandwidth + configuration of TE tunnels"; + uses te-tunnel-bandwidth-config; + uses te-tunnel-bandwidth-state; + } + + container auto-bandwidth { + when "../config/specification-type = 'AUTO'" { + description + "Include this container for auto bandwidth + specific configuration"; + } + description + "Parameters related to auto-bandwidth"; + + container config { + description + "Configuration parameters relating to MPLS + auto-bandwidth on the tunnel."; + uses te-lsp-auto-bandwidth-config; + } + + container state { + config false; + description + "State parameters relating to MPLS + auto-bandwidth on the tunnel."; + uses te-lsp-auto-bandwidth-config; + uses te-lsp-auto-bandwidth-state; + } + + container overflow { + description + "configuration of MPLS overflow bandwidth + adjustement for the LSP"; + + container config { + description + "Config information for MPLS overflow bandwidth + adjustment"; + uses te-lsp-overflow-config; + } + + container state { + config false; + description + "Config information for MPLS overflow bandwidth + adjustment"; + uses te-lsp-overflow-config; + } + } + + container underflow { + description + "configuration of MPLS underflow bandwidth + adjustement for the LSP"; + + container config { + description + "Config information for MPLS underflow bandwidth + adjustment"; + uses te-lsp-underflow-config; + } + + container state { + config false; + description + "State information for MPLS underflow bandwidth + adjustment"; + uses te-lsp-underflow-config; + } + } + } + } + } + + grouping p2p-path-candidate-secondary-path-config { + description + "Configuration parameters relating to a secondary path which + is a candidate for a particular primary path"; + + leaf secondary-path { + type leafref { + path "../../../../../../p2p-secondary-paths/" + + "p2p-secondary-path/config/name"; + } + description + "A reference to the secondary path that should be utilised + when the containing primary path option is in use"; + } + + leaf priority { + type uint16; + description + "The priority of the specified secondary path option. Higher + priority options are less preferable - such that a secondary + path reference with a priority of 0 is the most preferred"; + } + } + + grouping p2p-path-candidate-secondary-path-state { + description + "Operational state parameters relating to a secondary path + which is a candidate for a particular primary path"; + + leaf active { + type boolean; + description + "Indicates the current active path option that has + been selected of the candidate secondary paths"; + } + } + + grouping p2p-primary-paths-top { + description + "Top level grouping for p2p primary paths"; + + container p2p-primary-path { + description + "Primary paths associated with the LSP"; + + list p2p-primary-path { + key "name"; + description + "List of p2p primary paths for a tunnel"; + + leaf name { + type leafref { + path "../config/name"; + // Requires YANG 1.1 + //require-instance true; + } + description + "Path name"; + } + + container config { + description + "Configuration parameters related to paths"; + uses p2p-path-config; + } + + container state { + config false; + description + "State parameters related to paths"; + uses p2p-path-config; + uses p2p-path-state; + } + + container candidate-secondary-paths { + description + "The set of candidate secondary paths which may be used + for this primary path. When secondary paths are specified + in the list the path of the secondary LSP in use must be + restricted to those path options referenced. The + priority of the secondary paths is specified within the + list. Higher priority values are less preferred - that is + to say that a path with priority 0 is the most preferred + path. In the case that the list is empty, any secondary + path option may be utilised when the current primary path + is in use."; + + list candidate-secondary-path { + key "secondary-path"; + + description + "List of secondary paths which may be utilised when the + current primary path is in use"; + + leaf secondary-path { + type leafref { + path "../config/secondary-path"; + } + description + "A reference to the secondary path option reference + which acts as the key of the candidate-secondary-path + list"; + } + + container config { + description + "Configuration parameters relating to the candidate + secondary path"; + + uses p2p-path-candidate-secondary-path-config; + } + + container state { + config false; + description + "Operational state parameters relating to the candidate + secondary path"; + + uses p2p-path-candidate-secondary-path-config; + uses p2p-path-candidate-secondary-path-state; + } + } + } + + uses te-path-placement-constraints-top; + + } + } + } + + grouping p2p-secondary-paths-top { + description + "Top level grouping for p2p secondary paths"; + + container p2p-secondary-paths { + description + "Secondary paths for the LSP"; + + list p2p-secondary-path { + key "name"; + description + "List of p2p primary paths for a tunnel"; + + leaf name { + type leafref { + path "../config/name"; + // Requires YANG 1.1 + //require-instance true; + } + description + "Path name"; + } + + container config { + description + "Configuration parameters related to paths"; + uses p2p-path-config; + } + + container state { + config false; + description + "State parameters related to paths"; + uses p2p-path-config; + uses p2p-path-state; + } + + uses te-path-placement-constraints-top; + } + } + } + + grouping te-tunnels-top { + description + "Top level grouping for TE tunnels"; + + container tunnels { + description + "Enclosing container for tunnels"; + list tunnel { + key "name"; + description + "List of TE tunnels. This list contains only the LSPs that the + current device originates (i.e., for which it is the head-end). + Where the signaling protocol utilised for an LSP allows a mid-point + or tail device to be aware of the LSP (e.g., RSVP-TE), then the + associated sessions are maintained per protocol"; + + leaf name { + type leafref { + path "../config/name"; + // Requires YANG 1.1 + //require-instance true; + } + description + "The tunnel name"; + } + + container config { + description + "Configuration parameters related to TE tunnels:"; + uses te-global-tunnel-config; + } + + container state { + config false; + description + "State parameters related to TE tunnels"; + uses te-global-tunnel-config; + uses te-tunnel-state; + + } + + uses te-tunnel-bandwidth-top; + uses te-tunnel-p2p-top; + // TODO - add the p2mp configuration + } + } + } + +// data definition statements + +// augment statements + +// rpc statements + +// notification statements + +} diff --git a/models/yang/common/openconfig-mpls-types.yang b/models/yang/common/openconfig-mpls-types.yang new file mode 100644 index 0000000000..eb2d2d07d1 --- /dev/null +++ b/models/yang/common/openconfig-mpls-types.yang @@ -0,0 +1,432 @@ +module openconfig-mpls-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/mpls-types"; + + prefix "oc-mpls-types"; + + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "General types for MPLS / TE data model"; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // identity statements + + identity PATH_COMPUTATION_METHOD { + description + "base identity for supported path computation + mechanisms"; + } + + identity LOCALLY_COMPUTED { + base PATH_COMPUTATION_METHOD; + description + "indicates a constrained-path LSP in which the + path is computed by the local LER"; + } + + identity EXTERNALLY_QUERIED { + base PATH_COMPUTATION_METHOD; + description + "Constrained-path LSP in which the path is + obtained by querying an external source, such as a PCE server. + In the case that an LSP is defined to be externally queried, it may + also have associated explicit definitions (which are provided to the + external source to aid computation); and the path that is returned by + the external source is not required to provide a wholly resolved + path back to the originating system - that is to say, some local + computation may also be required"; + } + + identity EXPLICITLY_DEFINED { + base PATH_COMPUTATION_METHOD; + description + "constrained-path LSP in which the path is + explicitly specified as a collection of strict or/and loose + hops"; + } + + + // using identities rather than enum types to simplify adding new + // signaling protocols as they are introduced and supported + identity PATH_SETUP_PROTOCOL { + description + "base identity for supported MPLS signaling + protocols"; + } + + identity PATH_SETUP_RSVP { + base PATH_SETUP_PROTOCOL; + description + "RSVP-TE signaling protocol"; + } + + identity PATH_SETUP_SR { + base PATH_SETUP_PROTOCOL; + description + "Segment routing"; + } + + identity PATH_SETUP_LDP { + base PATH_SETUP_PROTOCOL; + description + "LDP - RFC 5036"; + } + + + identity PROTECTION_TYPE { + description + "base identity for protection type"; + } + + identity UNPROTECTED { + base PROTECTION_TYPE; + description + "no protection is desired"; + } + + identity LINK_PROTECTION_REQUIRED { + base PROTECTION_TYPE; + description + "link protection is desired"; + } + + identity LINK_NODE_PROTECTION_REQUESTED { + base PROTECTION_TYPE; + description + "node and link protection are both desired"; + } + + identity LSP_ROLE { + description + "Base identity for describing the role of + label switched path at the current node"; + } + + identity INGRESS { + base LSP_ROLE; + description + "Label switched path is an ingress (headend) + LSP"; + } + + identity EGRESS { + base LSP_ROLE; + description + "Label switched path is an egress (tailend) + LSP"; + } + + identity TRANSIT { + base LSP_ROLE; + description + "Label switched path is a transit LSP"; + } + + + identity TUNNEL_TYPE { + description + "Base identity from which specific tunnel types are + derived."; + } + + identity P2P { + base TUNNEL_TYPE; + description + "TE point-to-point tunnel type."; + } + + identity P2MP { + base TUNNEL_TYPE; + description + "TE point-to-multipoint tunnel type."; + } + + + identity LSP_OPER_STATUS { + description + "Base identity for LSP operational status"; + } + + identity DOWN { + base LSP_OPER_STATUS; + description + "LSP is operationally down or out of service"; + } + + identity UP { + base LSP_OPER_STATUS; + description + "LSP is operationally active and available + for traffic."; + } + + identity TUNNEL_ADMIN_STATUS { + description + "Base identity for tunnel administrative status"; + } + + identity ADMIN_DOWN { + base TUNNEL_ADMIN_STATUS; + description + "LSP is administratively down"; + } + + identity ADMIN_UP { + base TUNNEL_ADMIN_STATUS; + description + "LSP is administratively up"; + } + + identity NULL_LABEL_TYPE { + description + "Base identity from which specific null-label types are + derived."; + } + + identity EXPLICIT { + base NULL_LABEL_TYPE; + description + "Explicit null label is used."; + } + + identity IMPLICIT { + base NULL_LABEL_TYPE; + description + "Implicit null label is used."; + } + + identity LSP_METRIC_TYPE { + description + "Base identity for types of LSP metric specification"; + } + + identity LSP_METRIC_RELATIVE { + base LSP_METRIC_TYPE; + description + "The metric specified for the LSPs to which this identity refers + is specified as a relative value to the IGP metric cost to the + LSP's tail-end."; + } + + identity LSP_METRIC_ABSOLUTE { + base LSP_METRIC_TYPE; + description + "The metric specified for the LSPs to which this identity refers + is specified as an absolute value"; + } + + identity LSP_METRIC_INHERITED { + base LSP_METRIC_TYPE; + description + "The metric for for the LSPs to which this identity refers is + not specified explicitly - but rather inherited from the IGP + cost directly"; + } + + // typedef statements + typedef mpls-label { + type union { + type uint32 { + range 16..1048575; + } + type enumeration { + enum IPV4_EXPLICIT_NULL { + value 0; + description + "valid at the bottom of the label stack, + indicates that stack must be popped and packet forwarded + based on IPv4 header"; + } + enum ROUTER_ALERT { + value 1; + description + "allowed anywhere in the label stack except + the bottom, local router delivers packet to the local CPU + when this label is at the top of the stack"; + } + enum IPV6_EXPLICIT_NULL { + value 2; + description + "valid at the bottom of the label stack, + indicates that stack must be popped and packet forwarded + based on IPv6 header"; + } + enum IMPLICIT_NULL { + value 3; + description + "assigned by local LSR but not carried in + packets"; + } + enum ENTROPY_LABEL_INDICATOR { + value 7; + description + "Entropy label indicator, to allow an LSR + to distinguish between entropy label and applicaiton + labels RFC 6790"; + } + enum NO_LABEL { + description + "This value is utilised to indicate that the packet that + is forwarded by the local system does not have an MPLS + header applied to it. Typically, this is used at the + egress of an LSP"; + } + } + } + description + "type for MPLS label value encoding"; + reference "RFC 3032 - MPLS Label Stack Encoding"; + } + + typedef tunnel-type { + type enumeration { + enum P2P { + description + "point-to-point label-switched-path"; + } + enum P2MP { + description + "point-to-multipoint label-switched-path"; + } + enum MP2MP { + description + "multipoint-to-multipoint label-switched-path"; + } + } + description + "defines the tunnel type for the LSP"; + reference + "RFC 6388 - Label Distribution Protocol Extensions for + Point-to-Multipoint and Multipoint-to-Multipoint Label Switched + Paths + RFC 4875 - Extensions to Resource Reservation Protocol + - Traffic Engineering (RSVP-TE) for Point-to-Multipoint TE + Label Switched Paths (LSPs)"; + } + + typedef bandwidth-kbps { + type uint64; + units "Kbps"; + description + "Bandwidth values expressed in kilobits per second"; + } + + typedef bandwidth-mbps { + type uint64; + units "Mbps"; + description + "Bandwidth values expressed in megabits per second"; + } + + typedef bandwidth-gbps { + type uint64; + units "Gbps"; + description + "Bandwidth values expressed in gigabits per second"; + } + + typedef mpls-tc { + type uint8 { + range "0..7"; + } + description + "Values of the MPLS Traffic Class (formerly known as + Experimental, EXP) bits"; + } + + // grouping statements + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-mpls.yang b/models/yang/common/openconfig-mpls.yang new file mode 100644 index 0000000000..16dd4f9e68 --- /dev/null +++ b/models/yang/common/openconfig-mpls.yang @@ -0,0 +1,755 @@ +module openconfig-mpls { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/mpls"; + + prefix "oc-mpls"; + + + // import some basic types + import openconfig-mpls-types { prefix oc-mplst; } + import openconfig-mpls-rsvp { prefix oc-rsvp; } + import openconfig-mpls-ldp { prefix oc-ldp; } + import openconfig-types { prefix oc-types; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-segment-routing { prefix oc-sr; } + + // include submodules + include openconfig-mpls-te; + include openconfig-mpls-igp; + include openconfig-mpls-static; + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module provides data definitions for configuration of + Multiprotocol Label Switching (MPLS) and associated protocols for + signaling and traffic engineering. + + RFC 3031: Multiprotocol Label Switching Architecture + + The MPLS / TE data model consists of several modules and + submodules as shown below. The top-level MPLS module describes + the overall framework. Three types of LSPs are supported: + + i) traffic-engineered (or constrained-path) + + ii) IGP-congruent (LSPs that follow the IGP path) + + iii) static LSPs which are not signaled + + The structure of each of these LSP configurations is defined in + corresponding submodules. Companion modules define the relevant + configuration and operational data specific to key signaling + protocols used in operational practice. + + + +-------+ + +---------------->| MPLS |<--------------+ + | +-------+ | + | ^ | + | | | + +----+-----+ +--------+-------+ +-----+-----+ + | TE LSPs | | IGP-based LSPs | |static LSPs| + | | | | | | + +----------+ +----------------+ +-----------+ + ^ ^ ^ ^ + | +----------------+ | +--------+ + | | | | + | +------+ +-+---+-+ +--+--+ + +---+ RSVP | |SEGMENT| | LDP | + +------+ |ROUTING| +-----+ + +-------+ + "; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-07-02" { + description + "Add new RSVP-TE statistics, remove associated-rsvp-session + leaf. Remove use of date-and-time."; + reference "3.0.0"; + } + + revision "2018-06-16" { + description + "Included attributes for base LDP configuration."; + reference "2.6.0"; + } + + revision "2018-06-13" { + description + "Add ttl-propagation to global MPLS config"; + reference "2.5.0"; + } + + revision "2018-06-05" { + description + "Fixed bugs in when statements on RSVP-TE attributes"; + reference "2.4.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "2.4.1"; + } + + revision "2017-06-21" { + description + "Add TC bits typedef."; + reference "2.4.0"; + } + + revision "2017-03-22" { + description + "Add RSVP calculated-absolute-subscription-bw"; + reference "2.3.0"; + } + + revision "2017-01-26" { + description + "Add RSVP Tspec, clarify units for RSVP, remove unused LDP"; + reference "2.2.0"; + } + + revision "2016-12-15" { + description + "Add additional MPLS parameters"; + reference "2.1.0"; + } + + revision "2016-09-01" { + description + "Revisions based on implementation feedback"; + reference "2.0.0"; + } + + revision "2016-08-08" { + description + "Public release of MPLS models"; + reference "1.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + grouping mpls-admin-group-config { + description + "configuration data for MPLS link admin groups"; + + leaf admin-group-name { + type string; + description + "name for mpls admin-group"; + } + + leaf bit-position { + type uint32; + description + "bit-position value for mpls admin-group. The value + for the admin group is an integer that represents one + of the bit positions in the admin-group bitmask. Values + between 0 and 31 are interpreted as the original limit + of 32 admin groups. Values >=32 are interpreted as + extended admin group values as per RFC7308."; + } + + } + + grouping mpls-admin-groups-top { + + description + "top-level mpls admin-groups config + and state containers"; + + container mpls-admin-groups { + description + "Top-level container for admin-groups configuration + and state"; + + list admin-group { + key "admin-group-name"; + description + "configuration of value to name mapping + for mpls affinities/admin-groups"; + + leaf admin-group-name { + type leafref { + path "../config/admin-group-name"; + } + description + "name for mpls admin-group"; + } + container config { + description + "Configurable items for admin-groups"; + uses mpls-admin-group-config; + } + + container state { + config false; + description + "Operational state for admin-groups"; + uses mpls-admin-group-config; + } + } + } + } + + grouping mpls-te-igp-flooding-bandwidth-config { + description + "Configurable items for igp flooding bandwidth + threshold configuration."; + leaf threshold-type { + type enumeration { + enum DELTA { + description + "DELTA indicates that the local + system should flood IGP updates when a + change in reserved bandwidth >= the specified + delta occurs on the interface."; + } + enum THRESHOLD_CROSSED { + description + "THRESHOLD-CROSSED indicates that + the local system should trigger an update (and + hence flood) the reserved bandwidth when the + reserved bandwidth changes such that it crosses, + or becomes equal to one of the threshold values."; + } + } + description + "The type of threshold that should be used to specify the + values at which bandwidth is flooded. DELTA indicates that + the local system should flood IGP updates when a change in + reserved bandwidth >= the specified delta occurs on the + interface. Where THRESHOLD_CROSSED is specified, the local + system should trigger an update (and hence flood) the + reserved bandwidth when the reserved bandwidth changes such + that it crosses, or becomes equal to one of the threshold + values"; + } + + leaf delta-percentage { + when "../threshold-type = 'DELTA'" { + description + "The percentage delta can only be specified when the + threshold type is specified to be a percentage delta of + the reserved bandwidth"; + } + type oc-types:percentage; + description + "The percentage of the maximum-reservable-bandwidth + considered as the delta that results in an IGP update + being flooded"; + } + + leaf threshold-specification { + when "../threshold-type = 'THRESHOLD_CROSSED'" { + description + "The selection of whether mirrored or separate threshold + values are to be used requires user specified thresholds to + be set"; + } + type enumeration { + enum MIRRORED_UP_DOWN { + description + "MIRRORED_UP_DOWN indicates that a single set of + threshold values should be used for both increasing + and decreasing bandwidth when determining whether + to trigger updated bandwidth values to be flooded + in the IGP TE extensions."; + } + enum SEPARATE_UP_DOWN { + description + "SEPARATE_UP_DOWN indicates that a separate + threshold values should be used for the increasing + and decreasing bandwidth when determining whether + to trigger updated bandwidth values to be flooded + in the IGP TE extensions."; + } + } + description + "This value specifies whether a single set of threshold + values should be used for both increasing and decreasing + bandwidth when determining whether to trigger updated + bandwidth values to be flooded in the IGP TE extensions. + MIRRORED-UP-DOWN indicates that a single value (or set of + values) should be used for both increasing and decreasing + values, where SEPARATE-UP-DOWN specifies that the increasing + and decreasing values will be separately specified"; + } + + leaf-list up-thresholds { + when "../threshold-type = 'THRESHOLD_CROSSED'" + + "and ../threshold-specification = 'SEPARATE_UP_DOWN'" { + description + "A list of up-thresholds can only be specified when the + bandwidth update is triggered based on crossing a + threshold and separate up and down thresholds are + required"; + } + type oc-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth) at which bandwidth updates are to be + triggered when the bandwidth is increasing."; + } + + leaf-list down-thresholds { + when "../threshold-type = 'THRESHOLD_CROSSED'" + + "and ../threshold-specification = 'SEPARATE_UP_DOWN'" { + description + "A list of down-thresholds can only be specified when the + bandwidth update is triggered based on crossing a + threshold and separate up and down thresholds are + required"; + } + type oc-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth) at which bandwidth updates are to be + triggered when the bandwidth is decreasing."; + } + + leaf-list up-down-thresholds { + when "../threshold-type = 'THRESHOLD_CROSSED'" + + "and ../threshold-specification = 'MIRRORED_UP_DOWN'" { + description + "A list of thresholds corresponding to both increasing + and decreasing bandwidths can be specified only when an + update is triggered based on crossing a threshold, and + the same up and down thresholds are required."; + } + type oc-types:percentage; + description + "The thresholds (expressed as a percentage of the maximum + reservable bandwidth of the interface) at which bandwidth + updates are flooded - used both when the bandwidth is + increasing and decreasing"; + } + } + + + grouping mpls-te-igp-flooding-bandwidth { + description + "Top level group for traffic engineering + database flooding options"; + container igp-flooding-bandwidth { + description + "Interface bandwidth change percentages + that trigger update events into the IGP traffic + engineering database (TED)"; + container config { + description + "Configuration parameters for TED + update threshold "; + uses mpls-te-igp-flooding-bandwidth-config; + } + container state { + config false; + description + "State parameters for TED update threshold "; + uses mpls-te-igp-flooding-bandwidth-config; + } + } + } + + + grouping te-lsp-delay-config { + description + "Group for the timers goerning the delay + in installation and cleanup of TE LSPs"; + + leaf install-delay { + type uint16 { + range 0..3600; + } + units seconds; + description + "delay the use of newly installed te lsp for a + specified amount of time."; + } + + leaf cleanup-delay { + type uint16; + units seconds; + description + "delay the removal of old te lsp for a specified + amount of time"; + } + } + + grouping te-interface-attributes-top { + description + "Top level grouping for attributes + for TE interfaces."; + + list interface { + key "interface-id"; + description + "List of TE interfaces"; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "Reference to the interface id list key"; + } + + container config { + description + "Configuration parameters related to TE interfaces:"; + uses te-interface-attributes-config; + } + + container state { + config false; + description + "State parameters related to TE interfaces"; + uses te-interface-attributes-config; + } + + uses oc-if:interface-ref; + + uses mpls-te-igp-flooding-bandwidth; + } + } + + grouping te-interface-attributes-config { + description + "global level definitions for interfaces + on which TE is run"; + + leaf interface-id { + type oc-if:interface-id; + description + "Id of the interface"; + } + + leaf te-metric { + type uint32; + description + "TE specific metric for the link"; + } + + leaf-list srlg-membership { + type leafref { + path "../../../../te-global-attributes/srlgs/srlg/name"; + } + description + "list of references to named shared risk link groups that the + interface belongs to."; + } + + leaf-list admin-group { + type string; + description + "list of admin groups (by name) on the interface"; + } + } + + grouping mpls-te-lsp-timers { + description + "Grouping for traffic engineering timers"; + container te-lsp-timers { + description + "Definition for delays associated with setup + and cleanup of TE LSPs"; + + container config { + description + "Configuration parameters related + to timers for TE LSPs"; + + uses te-lsp-delay-config; + uses te-tunnel-reoptimize-config; + } + + container state { + config false; + description + "State related to timers for TE LSPs"; + + uses te-lsp-delay-config; + uses te-tunnel-reoptimize-config; + } + } + } + + grouping mpls-global-config { + description + "Definition of global MPLS configuration parameters"; + + leaf null-label { + type identityref { + base oc-mplst:NULL_LABEL_TYPE; + } + default oc-mplst:IMPLICIT; + description + "The null-label type used, implicit or explicit"; + } + + leaf ttl-propagation { + type boolean; + default true; + description + "Enables TTL propagation across the MPLS domain. + When ttl-propagation is set to true, the IP TTL + is copied into the MPLS header TTL when pushing + a label to an IP packet. If false, the IP TTL is + not copied into the MPLS header TTL and, therefore, + the IP TTL is not updated in the MPLS domain."; + } + + } + + grouping mpls-global-top { + description + "Top level grouping for global MPLS configuration "; + + container config { + description + "Top level global MPLS configuration"; + uses mpls-global-config; + } + + container state { + config false; + description + "Top level global MPLS state"; + uses mpls-global-config; + } + } + + grouping mpls-interfaces-top { + description + "Top level grouping for attributes + for MPLS-enabled interfaces."; + container interface-attributes { + description + "Parameters related to MPLS interfaces"; + list interface { + key "interface-id"; + description + "List of TE interfaces"; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "Reference to the interface id list key"; + } + + container config { + description + "Configuration parameters related to MPLS interfaces:"; + uses mpls-interface-attributes-config; + } + + container state { + config false; + description + "State parameters related to TE interfaces"; + uses mpls-interface-attributes-config; + } + + uses oc-if:interface-ref; + } + } + } + + grouping mpls-interface-attributes-config { + description + "global level definitions for interfaces + on which MPLS is run"; + + leaf interface-id { + type oc-if:interface-id; + description + "Indentifier for the MPLS interface"; + } + + leaf mpls-enabled { + type boolean; + default false; + description + "Enable MPLS forwarding on this interface"; + } + } + + grouping mpls-label-block-config { + description + "Configuration parameters relating to an MPLS label block."; + + leaf local-id { + type string; + description + "A local identifier for the global label block allocation."; + } + + leaf lower-bound { + type oc-mplst:mpls-label; + description + "Lower bound of the global label block. The block is defined to include + this label."; + } + + leaf upper-bound { + type oc-mplst:mpls-label; + description + "Upper bound for the global label block. The block is defined to include + this label."; + } + } + + grouping mpls-label-blocks-top { + description + "Top-level configuration and operational state parameters corresponding + to reserved label blocks."; + + container reserved-label-blocks { + description + "A range of labels starting with the start-label and up-to and including + the end label that should be allocated as reserved. These labels should + not be utilised by any dynamic label allocation on the local system unless + the allocating protocol is explicitly configured to specify that + allocation of labels should be out of the label block specified."; + + list reserved-label-block { + key "local-id"; + + description + "A range of labels starting with the start-label up to and including + the end label that should be allocated for use by a specific protocol."; + + leaf local-id { + type leafref { + path "../config/local-id"; + } + description + "A reference to a unique local identifier for this label block."; + } + + container config { + description + "Configuration parameters relating to the label block."; + + uses mpls-label-block-config; + } + + container state { + config false; + description + "State parameters relating to the label block."; + + uses mpls-label-block-config; + } + } + } + } + + grouping mpls-top { + description + "Top level grouping for MPLS configuration and state"; + + container mpls { + description + "Anchor point for mpls configuration and operational + data"; + + container global { + // entropy label support, label ranges will be added here. + description + "general mpls configuration applicable to any + type of LSP and signaling protocol - label ranges, + entropy label supportmay be added here"; + uses mpls-global-top; + uses mpls-interfaces-top; + uses mpls-label-blocks-top; + } + + container te-global-attributes { + description + "traffic-engineering global attributes"; + uses mpls-te-srlg-top; + uses mpls-admin-groups-top; + uses mpls-te-lsp-timers; + } + + container te-interface-attributes { + description + "traffic engineering attributes specific + for interfaces"; + uses te-interface-attributes-top; + } + + container signaling-protocols { + description + "top-level signaling protocol configuration"; + + uses oc-rsvp:rsvp-global; + uses oc-ldp:ldp-global; + uses oc-sr:sr-mpls-top; + } + + container lsps { + description + "LSP definitions and configuration"; + + container constrained-path { + description + "traffic-engineered LSPs supporting different + path computation and signaling methods"; + uses explicit-paths-top; + uses te-tunnels-top; + } + + container unconstrained-path { + description + "LSPs that use the IGP-determined path, i.e., non + traffic-engineered, or non constrained-path"; + + uses igp-lsp-common; + uses igp-lsp-setup; + } + + container static-lsps { + description + "statically configured LSPs, without dynamic + signaling"; + + uses static-lsp-top; + } + } + } + } + + // augment statements + + // rpc statements + + // notification statements +} diff --git a/models/yang/common/openconfig-network-instance-l2.yang b/models/yang/common/openconfig-network-instance-l2.yang new file mode 100644 index 0000000000..f41b57f501 --- /dev/null +++ b/models/yang/common/openconfig-network-instance-l2.yang @@ -0,0 +1,351 @@ +submodule openconfig-network-instance-l2 { + + belongs-to openconfig-network-instance { + prefix "oc-netinst"; + } + + // import some basic types + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-interfaces { prefix "oc-if"; } + import ietf-yang-types { prefix "yang"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module contains groupings which specifically relate to + Layer 2 network instance configuration and operational state + parameters."; + + oc-ext:openconfig-version "0.12.0"; + + revision "2019-04-16" { + description + "Move BGP RIB into the protocol/bgp container."; + reference "0.12.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.11.1"; + } + + revision "2018-08-11" { + description + "Add vlan id as additional key in MAC table"; + reference "0.11.0"; + } + + revision "2018-06-22" { + description + "Fix typo in OSPF when statement"; + reference "0.10.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements"; + reference "0.10.1"; + } + + revision "2018-02-19" { + description + "Add PIM and IGMP to network instance"; + reference "0.10.0"; + } + + revision "2017-12-13" { + description + "Fix incorrect constraint on SR and MPLS containers"; + reference "0.9.0"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes"; + reference "0.8.1"; + } + + revision "2017-02-28" { + description + "Add OSPFv2 to network instance"; + reference "0.8.0"; + } + + revision "2017-01-26" { + description + "Add policy forwarding to network instance"; + reference "0.7.0"; + } + + revision "2017-01-13" { + description + "Add AFT to the network instance"; + reference "0.6.0"; + } + + revision "2016-12-15" { + description + "Add segment routing to network instance"; + reference "0.5.0"; + } + + revision "2016-11-10" { + description + "Update model to include IS-IS."; + reference "0.4.1"; + } + + revision "2016-10-12" { + description + "Update table connections"; + reference "0.4.0"; + } + + revision "2016-09-28" { + description + "Change L2 instance to submodule; add MAC table"; + reference "0.3.0"; + } + + revision "2016-08-11" { + description + "Resolve repeated container names in routing protocols"; + reference "0.2.3"; + } + + revision "2016-07-08" { + description + "Updated with refactored routing protocol models"; + reference "0.2.1"; + } + + revision "2016-03-29" { + description + "Initial revision"; + reference "0.2.0"; + } + + revision "2015-11-20" { + description + "Initial revision"; + reference "0.1.0"; + } + + grouping l2ni-instance { + description + "Configuration and operational state parameters relating + to a Layer 2 network instance"; + + container fdb { + description + "Operational state and configuration parameters relating to + the forwarding database of the network instance"; + + container config { + description + "Configuration parameters relating to the FDB"; + uses l2ni-fdb-mac-config; + } + container state { + config false; + description + "Operational state parameters relating to the FDB"; + uses l2ni-fdb-mac-config; + } + + uses l2ni-mac-table-top; + } + } + + grouping l2ni-instance-common-config { + description + "Common configuration options which are specific to Layer 2 + network instances"; + + leaf mtu { + type uint16; + description + "The maximum frame size which should be supported for this + instance for Layer 2 frames"; + } + + } + + grouping l2ni-fdb-mac-config { + description + "Parameters relating to FDB behaviour relating to MAC + addresses"; + + leaf mac-learning { + type boolean; + description + "When this leaf is set to true, MAC learning is enabled for + the network instance, such that MAC addresses are learned + from ingress frames and added to the FDB."; + } + + leaf mac-aging-time { + // Cisco supports one aging time for local and remote, but + // can specify this time is absolute or against inactivity. + // ALU SROS supports different aging times for local and remote + // but does not allow absolute/inactivity specification. + // JNPR supports only a single aging time, and no specification + // of whether inactivity/absolute is used. + // It is easy to augment new options in here for local remote + // and an extra leaf to allow specification of the type of aging + // so this is left as a single value. + type uint16; + units seconds; + description + "The number of seconds of inactivity after which the entry + in the local FDB is timed out."; + } + + leaf maximum-entries { + type uint16; + description + "The maximum number of MAC address entries that should be + accepted into the FDB"; + } + } + + grouping l2ni-encapsulation-config { + description + "Encapsulation related configuration parameters for a L2 + network instance"; + + leaf control-word { + type boolean; + description + "Whether the control-word should be used for the network + instance"; + reference "RFC3985"; + } + } + + grouping l2ni-mac-table-config { + description + "Configuration data for MAC table entries"; + + leaf mac-address { + type yang:mac-address; + description + "MAC address for the dynamic or static MAC table + entry"; + } + + leaf vlan { + //TODO(aashaikh): Consider whether this should just reflect the + //VLAN id or be a union type to also support displaying/setting + //the VLAN by name (i.e., global VLAN configured in the VLAN + // model). + type leafref { + path "../../../../../../vlans/vlan/config/vlan-id"; + } + description + "VLAN on which the MAC address is present. The same MAC + address may be seen on multiple VLANs in some cases."; + } + } + + grouping l2ni-mac-table-state { + description + "Operational state data for MAC table entries"; + + leaf age { + type uint64; + units seconds; + description + "The time in seconds since the MAC address has been in the + table"; + } + + leaf entry-type { + type enumeration { + enum STATIC { + description + "Statically programmed MAC table entry"; + } + enum DYNAMIC { + description + "Dynamically learned MAC table entry"; + } + } + description + "Indicates whether the entry was statically configured, or + dynamically learned."; + } + + } + + grouping l2ni-mac-table-top { + description + "Top-level grouping for MAC table list"; + + + container mac-table { + description + "Table of learned or statically configured MAC addresses and + corresponding VLANs in the bridge domain"; + + container entries { + description + "Enclosing container for list of MAC table entries"; + + list entry { + key "mac-address vlan"; + description + "List of learned MAC addresses"; + + leaf mac-address { + type leafref { + path "../config/mac-address"; + } + description + "Reference to mac-address list key"; + } + + leaf vlan { + type leafref { + path "../config/vlan"; + } + description + "Reference to vlan list key"; + } + + container config { + description + "Configuration data for MAC table entries"; + + uses l2ni-mac-table-config; + } + + container state { + + config false; + + description + "Operational state data for MAC table entries"; + + uses l2ni-mac-table-config; + uses l2ni-mac-table-state; + } + + container interface { + description + "Reference to the base and/or subinterface for the + MAC table entry"; + + uses oc-if:interface-ref; + } + } + } + } + } +} diff --git a/models/yang/common/openconfig-network-instance-l3.yang b/models/yang/common/openconfig-network-instance-l3.yang new file mode 100644 index 0000000000..742f9002f8 --- /dev/null +++ b/models/yang/common/openconfig-network-instance-l3.yang @@ -0,0 +1,245 @@ +module openconfig-network-instance-l3 { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/network-instance-l3"; + + prefix "oc-ni-l3"; + + // import some basic types + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-types { prefix "octypes"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module contains groupings which specifically relate to + Layer 3 network instance configuration and operational state + parameters."; + + oc-ext:openconfig-version "0.11.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.11.1"; + } + + revision "2018-08-17" { + description + "Add a route limit for L3 network instances."; + reference "0.11.0"; + } + + revision "2017-12-13" { + description + "Fix incorrect constraint on SR and MPLS containers"; + reference "0.9.0"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes"; + reference "0.8.1"; + } + + revision "2017-02-28" { + description + "Add OSPFv2 to network instance"; + reference "0.8.0"; + } + + revision "2017-01-26" { + description + "Add policy forwarding to network instance"; + reference "0.7.0"; + } + + revision "2017-01-13" { + description + "Add AFT to the network instance"; + reference "0.6.0"; + } + + revision "2016-12-15" { + description + "Add segment routing to network instance"; + reference "0.5.0"; + } + + revision "2016-11-10" { + description + "Update model to include IS-IS."; + reference "0.4.1"; + } + + revision "2016-09-28" { + description + "Change L2 instance to submodule; add MAC table"; + reference "0.3.0"; + } + + revision "2016-08-11" { + description + "Resolve repeated container names in routing protocols"; + reference "0.2.3"; + } + + revision "2016-07-08" { + description + "Updated with refactored routing protocol models"; + reference "0.2.1"; + } + + revision "2016-03-29" { + description + "Initial revision"; + reference "0.2.0"; + } + + revision "2016-03-14" { + description + "Initial revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping l3ni-instance { + description + "Configuration and operational state parameters relevant + to network instances that include a Layer 3 type"; + + } + + grouping l3ni-instance-common-config { + description + "Configuration parameters that are common to L3 network + instances other than the default instance"; + + leaf-list enabled-address-families { + type identityref { + base octypes:ADDRESS_FAMILY; + } + description + "The address families that are to be enabled for this + network instance."; + } + } + + grouping l3ni-route-limit-structural { + description + "Configuration and state for the maximum number of routes + that should be used by routing instance."; + + container route-limits { + description + "Configuration and operational state relating to the + maximum number of routes for the address family that + should be allowed within the Layer 3 network instance. + + When the specified value is reached, no further prefixes + should be installed into the system's RIB from this network + instance unless the warning only leaf is set. In this case, + new routes should still be installed. If a alarm threshold + is specified, then this should be used to generate + alarms via telemetry for the network instance."; + + list route-limit { + key "afi"; + + description + "A route limit applying to a particular address family."; + + leaf afi { + type leafref { + path "../config/afi"; + } + description + "Reference to the address family for which the route + limit is being applied."; + } + + container config { + description + "Configuration options relating to the route limit."; + uses l3ni-route-limit-config; + } + + container state { + config false; + description + "Operational state parameters relating to the route limit."; + uses l3ni-route-limit-config; + uses l3ni-route-limit-state; + } + } + } + } + + grouping l3ni-route-limit-config { + description + "Configuration options relating to the route limit for a network + instance."; + + leaf afi { + type identityref { + base octypes:ADDRESS_FAMILY; + } + description + "The address family for which the route limit applies."; + } + + leaf maximum { + type uint32; + description + "The maximum number of routes for the address family. The + system should not install more than maximum number of + prefixes into the RIB unless the warning-only leaf is specified."; + } + + leaf warning-only { + type boolean; + default false; + description + "When specified, the route limit specified is considered only as + a warning - and routes should continue to be installed into the + RIB over the limit specified in the maximum leaf."; + } + + leaf alarm-threshold { + type uint32; + description + "When specified, an alarm should be generated when the threshold + number of installed routes is reached."; + } + } + + grouping l3ni-route-limit-state { + description + "Operational state relating to the route limit for a network + instance."; + + leaf threshold-exceeded { + type boolean; + description + "This leaf should be set to true in the case that the threshold + number of routes has been exceeded."; + } + + leaf installed-routes { + type uint32; + description + "The current number of routes installed for the address family."; + } + } +} diff --git a/models/yang/common/openconfig-network-instance-types.yang b/models/yang/common/openconfig-network-instance-types.yang new file mode 100644 index 0000000000..8ab176fa0f --- /dev/null +++ b/models/yang/common/openconfig-network-instance-types.yang @@ -0,0 +1,272 @@ +module openconfig-network-instance-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/network-instance-types"; + + prefix "oc-ni-types"; + + import openconfig-extensions { prefix "oc-ext"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Types associated with a network instance"; + + oc-ext:openconfig-version "0.8.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.8.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes"; + reference "0.8.1"; + } + + revision "2017-02-28" { + description + "Add OSPFv2 to network instance"; + reference "0.8.0"; + } + + revision "2017-01-26" { + description + "Add policy forwarding to network instance"; + reference "0.7.0"; + } + + revision "2017-01-13" { + description + "Add AFT to the network instance"; + reference "0.6.0"; + } + + revision "2016-12-15" { + description + "Add segment routing to network instance"; + reference "0.5.0"; + } + + revision "2016-11-10" { + description + "Update model to include IS-IS."; + reference "0.4.1"; + } + + revision "2016-10-12" { + description + "Update table connections"; + reference "0.4.0"; + } + + revision "2016-09-28" { + description + "Change L2 instance to submodule; add MAC table"; + reference "0.3.0"; + } + + revision "2016-08-11" { + description + "Resolve repeated container names in routing protocols"; + reference "0.2.3"; + } + + revision "2016-07-08" { + description + "Updated with refactored routing protocol models"; + reference "0.2.1"; + } + + revision "2016-03-29" { + description + "Initial revision"; + reference "0.2.0"; + } + + revision "2015-10-18" { + description + "Initial revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + identity NETWORK_INSTANCE_TYPE { + description + "A base identity which can be extended to indicate different + types of network instance supported by a device."; + } + + identity DEFAULT_INSTANCE { + base NETWORK_INSTANCE_TYPE; + description + "A special routing instance which acts as the 'default' or + 'global' routing instance for a network device."; + } + + identity L3VRF { + base NETWORK_INSTANCE_TYPE; + description + "A private Layer 3 only routing instance which is formed of + one or more RIBs"; + } + + identity L2VSI { + base NETWORK_INSTANCE_TYPE; + description + "A private Layer 2 only switch instance which is formed of + one or more L2 forwarding tables"; + } + + identity L2P2P { + base NETWORK_INSTANCE_TYPE; + description + "A private Layer 2 only forwarding instance which acts as + a point to point connection between two endpoints"; + } + + identity L2L3 { + base NETWORK_INSTANCE_TYPE; + description + "A private Layer 2 and Layer 2 forwarding instance"; + } + + identity ENDPOINT_TYPE { + description + "Specification of the type of endpoint that is being associated + with a network instance"; + } + + identity LOCAL { + base ENDPOINT_TYPE; + description + "A local interface which is being associated with the endpoint"; + } + + identity REMOTE { + base ENDPOINT_TYPE; + description + "A remote interface which is being associated with the + endpoint"; + } + + identity LABEL_ALLOCATION_MODE { + description + "Base identity to be used to express types of label allocation + strategies to be used within a network instance"; + } + + identity PER_PREFIX { + base LABEL_ALLOCATION_MODE; + description + "A label is to be allocated per prefix entry in the RIB for the + network instance"; + } + + identity PER_NEXTHOP { + base LABEL_ALLOCATION_MODE; + description + "A label is to be allocated per nexthop entry in the RIB for + the network instance"; + } + + identity INSTANCE_LABEL { + base LABEL_ALLOCATION_MODE; + description + "A single label is to be used for the instance"; + } + + identity ENCAPSULATION { + description + "On the wire encapsulations that can be used when + differentiating network instances"; + } + + identity MPLS { + base ENCAPSULATION; + description + "Use MPLS labels to distinguish network instances on the wire"; + } + + identity VXLAN { + base ENCAPSULATION; + description + "Use VXLAN (RFC7348) VNIs to distinguish network instances on + the wire"; + } + + identity SIGNALLING_PROTOCOL { + description + "The signalling protocol that should be used to diseminate + entries within a forwarding instance"; + } + + identity LDP { + base SIGNALLING_PROTOCOL; + description + "Use LDP-based setup for signalling. Where the instance is + a point-to-point service this refers to RFC4447 ('Martini') + setup. Where the service is an L2VSI, or L2L3 instance it + refers to RFC4762 LDP-signalled VPLS instances"; + } + + identity BGP_VPLS { + base SIGNALLING_PROTOCOL; + description + "Use BGP-based signalling and autodiscovery for VPLS instances + as per RFC4761"; + } + + identity BGP_EVPN { + base SIGNALLING_PROTOCOL; + description + "Use BGP-based Ethernet VPN (RFC7432) based signalling for + the network instance"; + } + + // rjs note: + // this should move to openconfig-types when merged + typedef route-distinguisher { + type union { + // type 0: <2-byte administrator>:<4-byte assigned number> + type string { + pattern '^(65[0-5][0-3][0-5]|[1-5][1-5][0-9][0-9][0-9]|' + + '[1-9]?[1-9]?[0-9][0-9]|[1-9]):' + + '(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-5]|' + + '[0-3][0-9]{9}|[1-9][0-9]{1,8}|[1-9])$'; + } + // type 1: :<2-byte assigned number> + 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]):' + + '(65[0-5][0-3][0-5]|[1-5][1-5][0-9][0-9][0-9]|' + + '[1-9]?[1-9]?[0-9][0-9]|[1-9])$'; + } + // type 2: <4-byte as-number>:<2-byte assigned number> + type string { + pattern + '^(4[0-2][0-9][0-4][0-9][0-6][0-7][0-2][0-9][0-5]|' + + '[0-3][0-9]{9}|[1-9][0-9]{1,8}|[1-9]):' + + '(65[0-5][0-3][0-5]|[1-5]{2}[0-9]{3}|' + + '[1-9]{0,2}[0-9][0-9]|[1-9])$'; + } + } + description "A route distinguisher value"; + reference "RFC4364"; + } +} diff --git a/models/yang/common/openconfig-ospf-types.yang b/models/yang/common/openconfig-ospf-types.yang new file mode 100644 index 0000000000..4ab7256eaf --- /dev/null +++ b/models/yang/common/openconfig-ospf-types.yang @@ -0,0 +1,795 @@ +module openconfig-ospf-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/ospf-types"; + + prefix "oc-ospf-types"; + + // import some basic types + import ietf-yang-types { prefix "yang"; } + import openconfig-extensions { prefix "oc-ext"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Type definitions for OSPF"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedefs + typedef ospf-area-identifier { + type union { + type uint32; + type yang:dotted-quad; + } + description + "An identifier for an area with OSPF version 2 or 3. This value + is expressed as either a dotted-quad, or a unsigned 32-bit + number"; + } + + typedef ospf-metric { + type uint16; + description + "A common type that can be utilised to express an OSPF metric"; + } + + typedef sr-sid-type { + type enumeration { + enum LABEL { + description + "When the length of the SR/Label Sub-TLV is specified to be 3, then + the right-most 20-bits represent a label value within the SR/Label + Sub-TLV. When this leaf is set to a value of Label the first-entry + leaf should be interpreted to be an MPLS label."; + } + enum SID { + description + "When the length of the SR/Label Sub-TLV is specified to be 4, then + the value specified in the first-entry leaf should be specified to + be a segment identifier."; + } + } + description + "A common type used to express the type of segment identifier that is + used in LSDB entries relating to segment routing"; + } + + // identities + identity OSPF_LSA_TYPE { + description + "Base identity for an OSPF LSA type. This identity is intended + to be used across both OSPFv2 and OSPFv3. Identity values that + correspond to only one OSPF version are marked as such."; + } + + identity ROUTER_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 1 - ROUTER_LSA. An LSA originated by each router within + the area describing the state and cost of the router's links + in the area."; + reference "RFC2328"; + } + + identity NETWORK_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 2 - NETWORK_LSA. An LSA originated for each broadcast and + non-broadcast multiple access (NBMA) in the area. This LSA is + originated by the designated router."; + reference "RFC2328"; + } + + identity SUMMARY_IP_NETWORK_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 3 - SUMMARY_IP_NETWORK_LSA. An LSA originated by area + border routers describing inter-area destinations. This LSA type + is used when the destination is an IP network"; + reference "RFC2328"; + } + + identity SUMMARY_ASBR_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 4 - SUMMARY_ASBR_LSA. An LSA originated by an area border + router describing inter-area destinations. This LSA type is used + when the destination is an AS boundary router."; + reference "RFC2328"; + } + + identity AS_EXTERNAL_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 5 - AS_EXTERNAL_LSA. This LSA type is used to describe + destinations external to the autonomous system, and is + originated by an AS boundary router (ASBR)."; + reference "RFC2328"; + } + + identity NSSA_AS_EXTERNAL_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 7 - NSSA_AS_EXTERNAL_LSA. This LSA type is used by + systems within a not-so-stubby-area (NSSA) to inject external + prefixes into the LSDB. They are translated to Type 5 LSAs + at an ABR device."; + reference "RFC3101"; + } + + identity OSPFV2_LINK_SCOPE_OPAQUE_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 9 - OSPFV2_LINK_SCOPE_OPAQUE_LSA. This LSA type is used + in OSPFv2 to distribute arbitrary information via the OSPF + protocol. The contents is specific to the application defining + the Opaque Type specified within the LSDB. LSAs with Type 9 have + a scope of the link that they are being transmitted on (and the + associated network or subnetwork)."; + reference "RFC5250"; + } + + identity OSPFV2_AREA_SCOPE_OPAQUE_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 10 - OSPFV2_AREA_SCOPE_OPAQUE_LSA. This LSA type is used + in OSPFv2 to distribute arbitrary information via the OSPF + protocol. The contents is specific to the application defining + the Opaque Type specified within the LSDB. LSAs with Type 10 have + a scope of the area that they are transmitted within."; + reference "RFC5250"; + } + + identity OSPFV2_AS_SCOPE_OPAQUE_LSA { + base "OSPF_LSA_TYPE"; + description + "Type 11 - OSPFV2_AS_SCOPE_OPAQUE_LSA. This LSA type is used + in OSPFv2 to distribute arbitrary information via the OSPF + protocol. The contents is specific to the application defining + the Opaque Type specified within the LSDB. LSAs with Type 11 + have a scope of the autonomous system that they are transmitted + within."; + reference "RFC5250"; + } + + identity ROUTER_LSA_TYPES { + description + "Sub-types of the router LSA"; + } + + identity ROUTER_LSA_P2P { + base "ROUTER_LSA_TYPES"; + description + "The LSA represents a point-to-point connection to another + router"; + } + + identity ROUTER_LSA_TRANSIT_NETWORK { + base "ROUTER_LSA_TYPES"; + description + "The LSA represents a connection to a transit network"; + } + + identity ROUTER_LSA_STUB_NETWORK { + base "ROUTER_LSA_TYPES"; + description + "The LSA represents a connection to a stub network"; + } + + identity ROUTER_LSA_VIRTUAL_LINK { + base "ROUTER_LSA_TYPES"; + description + "The LSA represents a virtual link connection"; + } + + identity OSPF_NEIGHBOR_STATE { + description + "The state of an adjacency between the local system and a remote + device"; + } + + identity DOWN { + base "OSPF_NEIGHBOR_STATE"; + description + "The initial state of a neighbor, indicating that no recent + information has been received from the neighbor."; + reference "RFC2328"; + } + + identity ATTEMPT { + base "OSPF_NEIGHBOR_STATE"; + description + "Utilised for neighbors that are attached to NBMA networks, it + indicates that no information has been recently received from + the neighbor but that Hello packets should be directly sent + to that neighbor."; + reference "RFC2328"; + } + + identity INIT { + base "OSPF_NEIGHBOR_STATE"; + description + "Indicates that a Hello packet has been received from the + neighbor but bi-directional communication has not yet been + established. That is to say that the local Router ID does + not appear in the list of neighbors in the remote system's + Hello packet."; + reference "RFC2328"; + } + + identity TWO_WAY { + base "OSPF_NEIGHBOR_STATE"; + description + "Communication between the local and remote system is + bi-directional such that the local system's Router ID is listed + in the received remote system's Hello packet."; + reference "RFC2328"; + } + + identity EXSTART { + base "OSPF_NEIGHBOR_STATE"; + description + "An adjacency with the remote system is being formed. The local + system is currently transmitting empty database description + packets in order to establish the master/slave relationship for + the adjacency."; + reference "RFC2328"; + } + + identity EXCHANGE { + base "OSPF_NEIGHBOR_STATE"; + description + "The local and remote systems are currently exchanging database + description packets in order to determine which elements of + their local LSDBs are out of date."; + reference "RFC2328"; + } + + identity LOADING { + base "OSPF_NEIGHBOR_STATE"; + description + "The local system is sending Link State Request packets to the + remote system in order to receive the more recently LSAs that + were discovered during the Exchange phase of the procedure + establishing the adjacency."; + reference "RFC2328"; + } + + identity FULL { + base "OSPF_NEIGHBOR_STATE"; + description + "The neighboring routers are fully adjacent such that both + LSDBs are synchronized. The adjacency will appear in Router and + Network LSAs"; + reference "RFC2328"; + } + + identity OSPF_NETWORK_TYPE { + description + "Types of network that OSPF should consider attached to an + interface"; + } + + identity POINT_TO_POINT_NETWORK { + base "OSPF_NETWORK_TYPE"; + description + "A interface that connects two routers."; + reference "RFC2328"; + } + + identity BROADCAST_NETWORK { + base "OSPF_NETWORK_TYPE"; + description + "An interface that supports >2 attached routers which has the + ability to address all connected systems via a single + (broadcast) address."; + } + + identity NON_BROADCAST_NETWORK { + base "OSPF_NETWORK_TYPE"; + description + "An interface that supports >2 attached rotuers which does not + have the ability to address all connected systems with a + broadcast address."; + } + + // rjs TODO: Maybe need p2mp here. + + + identity OSPF_OPAQUE_LSA_TYPE { + description + "This identity is the base used for opaque LSA types. The values + that extend this base are those that are described in the IANA + OSPF Opaque Link-State Advertisements (LSA) Option Types registry"; + } + + identity TRAFFIC_ENGINEERING { + base "OSPF_OPAQUE_LSA_TYPE"; + description + "The Traffic Engineering LSA. This type is used only with area-scope + Opaque LSAs - and is used to describe routers, point-to-point links + and connections to multi-access networks for traffic engineering + purposes."; + reference "RFC3630"; + } + + identity GRACE_LSA { + base "OSPF_OPAQUE_LSA_TYPE"; + description + "Grace LSAs are announced by a system undergoing graceful-restart. + A system that is attempting an OSPF graceful restart announces + Grace-LSAs with a specified grace period, indicating the intention + to have completed an restart within the specified period."; + reference "RFC3623"; + } + + identity ROUTER_INFORMATION { + base "OSPF_OPAQUE_LSA_TYPE"; + description + "The Router Information LSA is used by an OSPFv2 system to announce + optional capabilities of the local system, over and above those that + are included within the OSPF hello message field. The flooding scope + of the LSA can be link-, area-, or AS-wide (i.e., the LSA type can + be 9, 10 or 11)."; + reference "RFC7770"; + } + + identity OSPFV2_EXTENDED_PREFIX { + base "OSPF_OPAQUE_LSA_TYPE"; + description + "The Extended Prefix LSA is used in OSPFv2 to carry a set of attributes + that are to be associated with a prefix that is advertised in OSPF. The + attributes are carried as one or more TLV tuples. The flooding scope + of the LSA can be link-, area-, or AS-wide as specified by the + advertising system. The flooding scope of the LSA may exceed the scope + of the corresponding prefix."; + reference "RFC7684"; + } + + identity OSPFV2_EXTENDED_LINK { + base "OSPF_OPAQUE_LSA_TYPE"; + description + "The Extended Link LSA is used in OSPFv2 to carry a set of attributes + that are to be associated with a link that is advertised in OSPF. The + link attributes are carried as one or more TLV tuples. The flooding + scope of the link LSA is area-local - i.e., it is carried in a Type 10 + opaque LSA."; + reference "RFC7684"; + } + + identity OSPF_TE_LSA_TLV_TYPE { + description + "This identity is the base used for the type field of TLVs that are + included within the Traffic Engineering Opaque LSA."; + } + + identity TE_ROUTER_ADDRESS { + base "OSPF_TE_LSA_TLV_TYPE"; + description + "A stable IP address of the advertising router that is always reachable + if the node has connectivity."; + } + + identity TE_LINK { + base "OSPF_TE_LSA_TLV_TYPE"; + description + "A single link within a traffic engineering topology. A set of sub-TLVs + are carried within this attribute to indicate traffic engineering + characteristics of the link."; + } + + identity TE_ROUTER_IPV6_ADDRESS { + base "OSPF_TE_LSA_TLV_TYPE"; + description + "A stable IPv6 address of the advertising router that is always + reachable if the node has connectivity. This TLV is used only with + OSPFv3"; + reference "RFC5329"; + } + + identity TE_LINK_LOCAL { + base "OSPF_TE_LSA_TLV_TYPE"; + description + "Attributes associated with the local link by the system."; + reference "RFC4203"; + } + + identity TE_NODE_ATTRIBUTE { + base "OSPF_TE_LSA_TLV_TYPE"; + description + "Attributes associted with the local system"; + reference "RFC5786"; + } + + identity TE_OPTICAL_NODE_PROPERTY { + base "OSPF_TE_LSA_TLV_TYPE"; + description + "Attributes associated with the local optical node. A set of sub-TLVs + are carried within this TLV which are used within the GMPLS control + plane when using OSPF"; + } + + identity OSPF_TE_LINK_TLV_TYPE { + description + "This identity is the based used for the type field for sub-TLVs of the + Link TLV of the OSPF Traffic Engineering Opaque LSA"; + } + + identity TE_LINK_TYPE { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Link Type sub-TLV appears exactly once per OSPF-TE Link + and describes the type of the link"; + } + + identity TE_LINK_ID { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Link ID sub-TLV appears exactly once per OSPF-TE link and + identifies the remote end of the link."; + } + + identity TE_LINK_LOCAL_IP { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Local IP specifies a list of the interface addresses of the + local system corresponding to the traffic engineering link."; + } + + identity TE_LINK_REMOTE_IP { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Remote IP specifies a list of IP addresses of the remote + neighbors associated with the traffic engineering link."; + } + + identity TE_LINK_METRIC { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Metric specifies the link metric for traffic engineering + purposes"; + } + + identity TE_LINK_MAXIMUM_BANDWIDTH { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Maximum Bandwidth specifies the maximum bandwidth of the + link that it is associated with."; + } + + identity TE_LINK_MAXIMUM_RESERVABLE_BANDWIDTH { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE Maximum Reservable Bandwidth specifies the maximum + bandwidth that may be reserved on the link in bytes per second"; + } + + identity TE_LINK_UNRESERVED_BANDWIDTH { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE unreserved bandwidth indicates the amount of bandwidth + at each priority level that is currently not reserved"; + } + + identity TE_LINK_ADMIN_GROUP { + base "OSPF_TE_LINK_TLV_TYPE"; + description + "The OSPF-TE administrative group indicates the administrative group + that the is assigned to the interface"; + } + + identity TE_NODE_ATTRIBUTE_TLV_TYPE { + description + "This identity forms the base for sub-TLVs of the Node Attribute TLV + of the Traffic Engineering LSA"; + } + + identity NODE_IPV4_LOCAL_ADDRESS { + base "TE_NODE_ATTRIBUTE_TLV_TYPE"; + description + "The Node Attribute Sub-TLV contains a list of the IPv4 addresses of + the local system"; + } + + identity NODE_IPV6_LOCAL_ADDRESS { + base "TE_NODE_ATTRIBUTE_TLV_TYPE"; + description + "The Node Attribute Sub-TLV contains a list of the IPv6 addresses of + the local system"; + } + + identity GRACE_LSA_TLV_TYPES { + description + "This identity is used as the base for TLVs within the Grace LSA"; + } + + identity GRACE_PERIOD { + base "GRACE_LSA_TLV_TYPES"; + description + "This sub-TLV describes the period for which adjacencies should be + maintained with the restarting system"; + } + + identity GRACE_RESTART_REASON { + base "GRACE_LSA_TLV_TYPES"; + description + "This sub-TLV describes the reason for the OSPF restart of the system + that is restarting"; + } + + identity GRACE_IP_INTERFACE_ADDRESS { + base "GRACE_LSA_TLV_TYPES"; + description + "This sub-TLV specifies the restarting system's IP address on the + interface via which it is advertising the Grace LSA"; + } + + identity RI_LSA_TLV_TYPES { + description + "This identity is used as the base for the TLVs within the Router + Information LSA"; + reference "RFC7770"; + } + + identity RI_INFORMATIONAL_CAPABILITIES { + base "RI_LSA_TLV_TYPES"; + description + "Informational capabilities of the advertising system"; + reference "RFC7770"; + } + + identity RI_FUNCTIONAL_CAPABILITIES { + base "RI_LSA_TLV_TYPES"; + description + "Functional capabilities of the advertising system"; + reference "RFC7770"; + } + + identity RI_NODE_ADMIN_TAG { + base "RI_LSA_TLV_TYPES"; + description + "Operator-defined administrative tags associated with the advertising + system"; + reference "RFC7777"; + } + + identity RI_SR_SID_LABEL_RANGE { + base "RI_LSA_TLV_TYPES"; + description + "SID or Label ranges for use with segment routing when forwarding to + the advertising system"; + reference "draft-ietf-ospf-segment-routing-extensions"; + } + + identity RI_SR_ALGORITHM { + base "RI_LSA_TLV_TYPES"; + description + "The algorithms that are supported for segment routing by the + advertising system"; + reference "draft-ietf-ospf-segment-routing-extensions"; + } + + // will be shared with IS-IS + identity SR_ALGORITHM { + description + "This identity is used as a base for the algorithms that can be + supported for segment routing and are advertised by a system in the RI + LSA"; + } + + identity SPF { + base "SR_ALGORITHM"; + description + "The standard shortest path algorithm based on link metric, + as used by the OSPF protocol"; + } + + identity STRICT_SPF { + base "SR_ALGORITHM"; + description + "The standard shortest path algorithm based on link metric, with the + requirement that all nodes along the path honor the SPF decision. That + is to say that the SPF decision cannot be altered by local policy at + the node"; + } + + identity OSPF_RI_SR_SID_LABEL_TLV_TYPES { + description + "This identity is used as a base for the sub-TLVs of the Segment + Routing SID/Label Range TLV"; + } + + identity SR_SID_LABEL_TLV { + base "OSPF_RI_SR_SID_LABEL_TLV_TYPES"; + description + "A range of SID/Label values used by the local system"; + reference "draft-ietf-ospf-segment-routing-extensions"; + } + + identity OSPFV2_ROUTER_LINK_TYPE { + description + "OSPFv2 Router Link Types as per the IANA registry defined in + RFC2740"; + } + + identity POINT_TO_POINT_LINK { + base "OSPFV2_ROUTER_LINK_TYPE"; + description + "The link is a point-to-point connection to another router"; + } + + identity TRANSIT_NETWORK_LINK { + base "OSPFV2_ROUTER_LINK_TYPE"; + description + "The link is a connection to a transit network"; + } + + identity STUB_NETWORK_LINK { + base "OSPFV2_ROUTER_LINK_TYPE"; + description + "The link is a connection to a stub network"; + } + + identity VIRTUAL_LINK { + base "OSPFV2_ROUTER_LINK_TYPE"; + description + "The link is a virtual connection to another router"; + } + + identity OSPFV2_EXTENDED_PREFIX_SUBTLV_TYPE { + description + "Sub-TLVs of the OSPFv2 Extended Prefix LSA as defined by + RFC7684"; + } + + identity EXTENDED_PREFIX_RANGE { + base "OSPFV2_EXTENDED_PREFIX_SUBTLV_TYPE"; + description + "The attributes being described relate to a range of prefixes"; + } + + identity PREFIX_SID { + base "OSPFV2_EXTENDED_PREFIX_SUBTLV_TYPE"; + description + "The TLV describes a Segment Routing Prefix Segment Identifier + associated with a prefix"; + } + + identity SID_LABEL_BINDING { + base "OSPFV2_EXTENDED_PREFIX_SUBTLV_TYPE"; + description + "The TLV describes a binding of a SID to a path to the prefix, + which may have associated path characteristics"; + } + + identity OSPFV2_EXTENDED_PREFIX_SID_LABEL_BINDING_SUBTLV_TYPE { + description + "Sub-TLV types carried in the SID/Label Binding Sub-TLV of + the Extended Prefix Sub-TLV"; + } + + identity SID_MPLS_LABEL_BINDING { + base "OSPFV2_EXTENDED_PREFIX_SID_LABEL_BINDING_SUBTLV_TYPE"; + description + "This sub-TLV indicates a binding between an SR SID and an + MPLS label and must be present in the sub-TLV"; + } + + identity ERO_METRIC { + base "OSPFV2_EXTENDED_PREFIX_SID_LABEL_BINDING_SUBTLV_TYPE"; + description + "This sub-TLV indicates the cost of the ERO path being + advertised in the SID/Label TLV"; + } + + identity ERO_PATH { + base "OSPFV2_EXTENDED_PREFIX_SID_LABEL_BINDING_SUBTLV_TYPE"; + description + "This sub-TLV indicates the path associated with an ERO + being advertised in the SID/Label TLV"; + } + + identity OSPFV2_EXTPREFIX_BINDING_ERO_PATH_SEGMENT_TYPE { + description + "The types of segment included within an ERO Path described + within the SID/Label binding sub-TLV"; + } + + identity IPV4_SEGMENT { + base "OSPFV2_EXTPREFIX_BINDING_ERO_PATH_SEGMENT_TYPE"; + description + "The segment is specified as an IPv4 address"; + } + + identity UNNUMBERED_INTERFACE_SEGMENT { + base "OSPFV2_EXTPREFIX_BINDING_ERO_PATH_SEGMENT_TYPE"; + description + "The segment is specified as an unnumbered interface of + a remote system"; + } + + identity OSPFV2_EXTENDED_LINK_SUBTLV_TYPE { + description + "Sub-TLVs of the Extended Link TLV for OSPFv2"; + } + + identity ADJACENCY_SID { + base "OSPFV2_EXTENDED_LINK_SUBTLV_TYPE"; + description + "The extended link sub-TLV indicates an Adjacency SID"; + } + + identity MAX_METRIC_TRIGGER { + description + "Triggers which cause the maximum metric to be set for + entities advertised in OSPF"; + } + + identity MAX_METRIC_ON_SYSTEM_BOOT { + base "MAX_METRIC_TRIGGER"; + description + "Set the maximum metric when the system boots."; + } + + identity MAX_METRIC_INCLUDE { + description + "Entities that may optionally be included when advertising + the maximum metric."; + } + + identity MAX_METRIC_INCLUDE_STUB { + base "MAX_METRIC_INCLUDE"; + description + "Include stub networks when advertising the maximum metric."; + } + + identity MAX_METRIC_INCLUDE_TYPE2_EXTERNAL { + base "MAX_METRIC_INCLUDE"; + description + "Include OSPF Type 2 external routes when advertising + the maximum metric."; + } +} diff --git a/models/yang/common/openconfig-ospfv2-area-interface.yang b/models/yang/common/openconfig-ospfv2-area-interface.yang new file mode 100644 index 0000000000..31da1831eb --- /dev/null +++ b/models/yang/common/openconfig-ospfv2-area-interface.yang @@ -0,0 +1,478 @@ +submodule openconfig-ospfv2-area-interface { + + belongs-to openconfig-ospfv2 { + prefix "oc-ospfv2"; + } + + import ietf-yang-types { prefix "yang"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-types { prefix "oc-types"; } + import openconfig-interfaces { prefix "oc-if"; } + import openconfig-ospf-types { prefix "oc-ospf-types"; } + + // include common submodule + include openconfig-ospfv2-common; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule provides OSPFv2 configuration and operational + state parameters that are specific to the area context"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping ospfv2-area-interface-config { + description + "Configuration parameters for an OSPF interface"; + + leaf id { + type string; + description + "An operator-specified string utilised to uniquely + reference this interface"; + } + + leaf network-type { + type identityref { + base "oc-ospf-types:OSPF_NETWORK_TYPE"; + } + description + "The type of network that OSPFv2 should use for the specified + interface."; + } + + leaf priority { + type uint8; + description + "The local system's priority to become the designated + router"; + } + + leaf multi-area-adjacency-primary { + type boolean; + default true; + description + "When the specified interface is included in more than one + area's configuration, this leaf marks whether the area should + be considered the primary (when the value is true). In the + case that this value is false, the area is considered a + secondary area."; + } + + leaf authentication-type { + type string; + // rjs TODO: discuss with bogdanov@ what the approach for auth + // links should be. + description + "The type of authentication that should be used on this + interface"; + } + + leaf metric { + type oc-ospf-types:ospf-metric; + description + "The metric for the interface"; + } + + leaf passive { + type boolean; + description + "When this leaf is set to true, the interface should be + advertised within the OSPF area but OSPF adjacencies should + not be established over the interface"; + } + + leaf hide-network { + type boolean; + description + "When this leaf is set to true, the network connected to + the interface should be hidden from OSPFv2 advertisements + per the procedure described in RFC6860."; + reference + "RFC6860 - Hiding Transit-Only Networks in OSFF"; + } + } + + grouping ospfv2-area-interface-timers-config { + description + "Configuration parameters relating to per-interface OSPFv2 + timers"; + + leaf dead-interval { + type uint32; + units seconds; + description + "The number of seconds that the local system should let + elapse before declaring a silent router down"; + reference "RFC2328"; + } + + leaf hello-interval { + type uint32; + units seconds; + description + "The number of seconds the local system waits between the + transmission of subsequent Hello packets"; + } + + leaf retransmission-interval { + type uint32; + units seconds; + description + "The number of seconds that the local system waits before + retransmitting an unacknowledged LSA."; + } + } + + grouping ospfv2-area-interface-mpls-config { + description + "Configuration parameters relating to MPLS extensions for OSPF"; + + leaf traffic-engineering-metric { + type uint32; + description + "A link metric that should only be considered for traffic + engineering purposes."; + reference "RFC3630, §2.5.5"; + } + } + + grouping ospfv2-area-interface-neighbor-config { + description + "Configuration parameters relating to an individual neighbor + system on an interface within an OSPF area"; + + leaf router-id { + type yang:dotted-quad; + description + "The router ID of the remote system."; + } + + leaf metric { + type oc-ospf-types:ospf-metric; + description + "The metric that should be considered to the remote neighbor + over this interface. This configuration is only applicable + for multiple-access networks"; + } + } + + grouping ospfv2-area-interface-neighbor-state { + description + "Operational state parameters relating an individual neighbor + system on an interface within an OSPF area"; + + leaf priority { + type uint8; + description + "The remote system's priority to become the designated + router"; + } + + leaf dead-time { + // rjs TODO: discussion with aashaikh@ + hines@ around how this + // value should be represented, usually is a timer that + // continually counts down but this sounds challenging for + // telemetry. + type oc-types:timeticks64; + description + "The time at which this neighbor's adjacency will be + considered dead. This value is expressed as a number of + seconds since the Unix Epoch"; + } + + leaf designated-router { + type yang:dotted-quad; + description + "The designated router for the adjacency. This device + advertises the Network LSA for broadcast and NBMA networks."; + } + + leaf backup-designated-router { + type yang:dotted-quad; + description + "The backup designated router for the adjacency."; + } + + leaf optional-capabilities { + // rjs TODO: should this be anything more than the hex-string + // this is currently what is shown in IOS/JUNOS + type yang:hex-string; + description + "The optional capabilities field received in the Hello + message from the neighbor"; + } + + leaf last-established-time { + type oc-types:timeticks64; + units seconds; + // rjs TODO: check implementations - is FULL considered 'up' + // since the adjacency is probably up since ExStart + description + "The time at which the adjacency was last established with + the neighbor. That is to say the time at which the + adjacency last transitioned into the FULL state. + + This value is expressed as the number of seconds, relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + leaf adjacency-state { + type identityref { + base "oc-ospf-types:OSPF_NEIGHBOR_STATE"; + } + description + "The state of the adjacency with the neighbor."; + } + + leaf state-changes { + type uint32; + description + "The number of transitions out of the FULL state that this + neighbor has been through"; + } + + leaf retranmission-queue-length { + type uint32; + description + "The number of LSAs that are currently in the queue to be + retransmitted to the neighbor"; + } + } + + grouping ospfv2-area-interface-lsa-filter-config { + description + "Configuration options relating to filtering LSAs + on an interface."; + + leaf all { + type boolean; + description + "When this leaf is set to true, all LSAs should be + filtered to the neighbours with whom adjacencies are + formed on the interface."; + } + + // NB: this container can be augmented to add additional + // filtering options which exist in some implementations. + } + + grouping ospfv2-area-interface-mpls-igp-ldp-sync-state { + description + "Operational state parameters relating to MPLS LDP/IGP + synchronization on a per-neighbor basis"; + + leaf synchronized { + type boolean; + description + "When the value of this leaf is set to true, the + LDP neighbors reachable via this interface are considered + to be synchronized, and hence the link is considered + usable by the IGP."; + } + } + + grouping ospfv2-area-interfaces-structure { + description + "Structural grouping for configuration and operational state + parameters that relate to an interface"; + + container interfaces { + description + "Enclosing container for a list of interfaces enabled within + this area"; + + list interface { + key "id"; + + description + "List of interfaces which are enabled within this area"; + + leaf id { + type leafref { + path "../config/id"; + } + description + "A pointer to the identifier for the interface."; + } + + container config { + description + "Configuration parameters for the interface on which + OSPFv2 is enabled"; + + uses ospfv2-area-interface-config; + } + + container state { + config false; + description + "Operational state parameters for the interface on which + OSPFv2 is enabled"; + uses ospfv2-area-interface-config; + } + + uses oc-if:interface-ref; + + container timers { + description + "Timers relating to OSPFv2 on the interface"; + + container config { + description + "Configuration parameters for OSPFv2 timers on the + interface"; + uses ospfv2-area-interface-timers-config; + } + + container state { + config false; + description + "Operational state parameters for OSPFv2 timers on + the interface"; + + uses ospfv2-area-interface-timers-config; + } + } + + container mpls { + description + "Configuration and operational state parameters for + OSPFv2 extensions related to MPLS on the interface."; + + container config { + description + "Configuration parameters for OSPFv2 extensions relating + to MPLS for the interface"; + uses ospfv2-area-interface-mpls-config; + } + + container state { + config false; + description + "Operational state for OSPFv2 extensions relating to + MPLS for the interface"; + uses ospfv2-area-interface-mpls-config; + } + + container igp-ldp-sync { + description + "OSPFv2 parameters relating to LDP/IGP synchronization"; + + container config { + description + "Configuration parameters relating to LDP/IG + synchronization."; + uses ospfv2-common-mpls-igp-ldp-sync-config; + } + + container state { + config false; + description + "Operational state variables relating to LDP/IGP + synchronization"; + uses ospfv2-common-mpls-igp-ldp-sync-config; + uses ospfv2-area-interface-mpls-igp-ldp-sync-state; + } + } + } + + container lsa-filter { + description + "OSPFv2 parameters relating to filtering of LSAs to + neighbors the specified interface."; + + container config { + description + "Configuration parameters relating to filtering LSAs + on the specified interface."; + uses ospfv2-area-interface-lsa-filter-config; + } + + container state { + config false; + description + "Operational state parameters relating to filtering + LSAs on the specified interface"; + uses ospfv2-area-interface-lsa-filter-config; + } + } + + container neighbors { + description + "Enclosing container for the list of neighbors that + an adjacency has been established with on the interface"; + + list neighbor { + key "router-id"; + + description + "A neighbor with which an OSPFv2 adjacency has been + established within this area"; + + leaf router-id { + type leafref { + path "../config/router-id"; + } + description + "Reference to the router ID of the adjacent system"; + } + + container config { + description + "Configuration parameters relating to the adjacent + system"; + uses ospfv2-area-interface-neighbor-config; + } + + container state { + config false; + description + "Operational state parameters relating to the adjacent + system"; + uses ospfv2-area-interface-neighbor-config; + uses ospfv2-area-interface-neighbor-state; + } + } + } + + } + } + } + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-ospfv2-area.yang b/models/yang/common/openconfig-ospfv2-area.yang new file mode 100644 index 0000000000..4d0df75c4c --- /dev/null +++ b/models/yang/common/openconfig-ospfv2-area.yang @@ -0,0 +1,175 @@ +submodule openconfig-ospfv2-area { + + belongs-to openconfig-ospfv2 { + prefix "oc-ospfv2"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-ospf-types { prefix "oc-ospf-types"; } + import ietf-inet-types { prefix "inet"; } + + // include other required submodules + include openconfig-ospfv2-area-interface; + include openconfig-ospfv2-lsdb; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule provides OSPFv2 configuration and operational + state parameters that are specific to the area context"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping ospfv2-area-config { + description + "Configuration parameters relating to an OSPF area"; + + leaf identifier { + type oc-ospf-types:ospf-area-identifier; + description + "An identifier for the OSPFv2 area - described as either a + 32-bit unsigned integer, or a dotted-quad"; + } + } + + grouping ospfv2-area-mpls-config { + description + "Configuration parameters relating to OSPFv2 extensions for + MPLS"; + + leaf traffic-engineering-enabled { + type boolean; + description + "Specifies whether traffic engineering extensions should be + advertised within the area"; + } + } + + grouping ospfv2-area-virtual-link-config { + description + "Configuration parameters relating to a virtual-link within + the OSPF area"; + + leaf remote-router-id { + type inet:ipv4-address-no-zone; + description + "The router ID of the device which terminates the remote end + of the virtual link"; + } + } + + grouping ospfv2-area-structure { + description + "Structural grouping for configuration and operational state + parameters that relate to an individual area"; + + container config { + description + "Configuration parameters relating to an OSPFv2 area"; + + uses ospfv2-area-config; + } + + container state { + config false; + description + "Operational state parameters relating to an OSPFv2 area"; + uses ospfv2-area-config; + } + + container mpls { + description + "Configuration and operational state parameters for OSPFv2 + extensions relating to MPLS"; + + container config { + description + "Configuration parameters relating to MPLS extensions for + OSPFv2"; + uses ospfv2-area-mpls-config; + } + + container state { + config false; + description + "Operational state parameters relating to MPLS extensions + for OSPFv2"; + uses ospfv2-area-mpls-config; + } + } + + uses ospfv2-lsdb-structure; + uses ospfv2-area-interfaces-structure; + + container virtual-links { + description + "Configuration and state parameters relating to virtual + links from the source area to a remote router"; + + list virtual-link { + key "remote-router-id"; + + description + "Configuration and state parameters relating to a + virtual link"; + + leaf remote-router-id { + type leafref { + path "../config/remote-router-id"; + } + description + "Reference to the remote router ID"; + } + + container config { + description + "Configuration parameters relating to the OSPF virtual link"; + uses ospfv2-area-virtual-link-config; + } + + container state { + config false; + description + "State parameters relating to the OSPF virtual link"; + uses ospfv2-area-virtual-link-config; + uses ospfv2-area-interface-neighbor-state; + } + } + } + } +} diff --git a/models/yang/common/openconfig-ospfv2-common.yang b/models/yang/common/openconfig-ospfv2-common.yang new file mode 100644 index 0000000000..64c7e9d08d --- /dev/null +++ b/models/yang/common/openconfig-ospfv2-common.yang @@ -0,0 +1,97 @@ +submodule openconfig-ospfv2-common { + + belongs-to openconfig-ospfv2 { + prefix "oc-ospfv2"; + } + + import openconfig-extensions { prefix "oc-ext"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule provides OSPFv2 configuration and operational + state parameters that are shared across multiple contexts"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping ospfv2-common-mpls-igp-ldp-sync-config { + description + "Configuration parameters used for OSPFv2 MPLS/IGP + synchronization"; + + leaf enabled { + type boolean; + description + "When this leaf is set to true, do not utilise this link for + forwarding via the IGP until such time as LDP adjacencies to + the neighbor(s) over the link are established."; + } + + leaf post-session-up-delay { + type uint32; + units milliseconds; + description + "This leaf specifies a delay, expressed in units of milliseconds, + between the LDP session to the IGP neighbor being established, and + it being considered synchronized by the IGP."; + } + } + + grouping ospfv2-common-timers { + description + "Common definition of the type of timers that the OSPFv2 implementation + uses"; + + leaf timer-type { + type enumeration { + enum LINEAR_BACKOFF { + description + "The backoff used by the OSPFv2 implementation is linear, such that + a common delay is added following each event."; + } + enum EXPONENTIAL_BACKOFF { + description + "The backoff used by the OSPFv2 implementation is exponential, such + that the delay added following each event increases."; + } + } + description + "The timer mode that is utilised by the implementation."; + } + } +} \ No newline at end of file diff --git a/models/yang/common/openconfig-ospfv2-global.yang b/models/yang/common/openconfig-ospfv2-global.yang new file mode 100644 index 0000000000..c714fe2802 --- /dev/null +++ b/models/yang/common/openconfig-ospfv2-global.yang @@ -0,0 +1,515 @@ +submodule openconfig-ospfv2-global { + + belongs-to openconfig-ospfv2 { + prefix "oc-ospfv2"; + } + + import ietf-yang-types { prefix "yang"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-routing-policy { prefix "oc-rpol"; } + import openconfig-ospf-types { prefix "oc-ospft"; } + + // Include common submodule + include openconfig-ospfv2-common; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule provides OSPFv2 configuration and operational + state parameters that are global to a particular OSPF instance"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping ospfv2-global-config { + description + "Global configuration for OSPFv2"; + + leaf router-id { + type yang:dotted-quad; + description + "A 32-bit number represented as a dotted quad assigned to + each router running the OSPFv2 protocol. This number should + be unique within the autonomous system"; + reference "rfc2828"; + } + + leaf summary-route-cost-mode { + type enumeration { + enum RFC1583_COMPATIBLE { + description + "Specify that summary routes should assume the cost of + the lowest-cost more-specific route as per the behaviour + specified in RFC1583"; + } + enum RFC2328_COMPATIBLE { + description + "Specify that summary routes should assume the cost of the + highest-cost more-specific route as per the revised + behaviour specified in RFC2328"; + } + } + default "RFC2328_COMPATIBLE"; + description + "Specify how costs for the summary routes should be specified + as per the behaviour in the original OSPF specification + RFC1583, or alternatively whether the revised behaviour + described in RFC2328 should be utilised"; + } + + leaf igp-shortcuts { + type boolean; + description + "When this leaf is set to true, OSPFv2 will route traffic to + a remote system via any LSP to the system that is marked as + shortcut eligible."; + } + + leaf log-adjacency-changes { + type boolean; + description + "When this leaf is set to true, a log message will be + generated when the state of an OSPFv2 neighbour changes."; + } + + leaf hide-transit-only-networks { + type boolean; + description + "When this leaf is set to true, do not advertise prefixes + into OSPFv2 that correspond to transit interfaces, as per + the behaviour discussed in RFC6860."; + reference + "RFC6860 - Hiding Transit-Only Networks in OSPF"; + } + } + + grouping ospfv2-global-spf-timers-config { + description + "Configuration parameters relating to global SPF timer + parameters for OSPFv2"; + + leaf initial-delay { + // rjs TODO: IS-IS model has this as decimal64 - should it be + // that or uint32 msec? + type uint32; + units msec; + description + "The value of this leaf specifies the time between a change + in topology being detected and the first run of the SPF + algorithm."; + } + + leaf maximum-delay { + // rjs TODO: same question as above + type uint32; + units msec; + description + "The value of this leaf specifies the maximum delay between + a topology change being detected and the SPF algorithm + running. This value is used for implementations that support + increasing the wait time between SPF runs."; + } + + // rjs TODO: some questions here around what we should specify: + // JUNOS has rapid-runs and holddown + // Cisco has maximum time between runs, and then a doubling of + // the wait interval up to that maximum. + // ALU has first-wait, second-wait, max-wait + } + + grouping ospfv2-global-lsa-generation-timers-config { + description + "Configuration parameters relating to global LSA generation + parameters for OSPFv2"; + + leaf initial-delay { + type uint32; + units msec; + description + "The value of this leaf specifies the time between the first + time an LSA is generated and advertised and the subsequent + generation of that LSA."; + } + + leaf maximum-delay { + type uint32; + units msec; + description + "The value of this leaf specifies the maximum time between the + generation of an LSA and the subsequent re-generation of that + LSA. This value is used in implementations that support + increasing delay between generation of an LSA"; + } + } + + grouping ospfv2-global-spf-timers-state { + description + "Operational state parameters relating to OSPFv2 global + timers"; + + uses ospfv2-common-timers; + } + + grouping ospfv2-global-lsa-generation-timers-state { + description + "Operational state parameters relating to OSPFv2 global + timers"; + + uses ospfv2-common-timers; + } + + grouping ospfv2-global-graceful-restart-config { + description + "Configuration parameters relating to graceful restart for + OSPFv2"; + + leaf enabled { + type boolean; + description + "When the value of this leaf is set to true, graceful restart + is enabled on the local system. In this case, the system will + use Grace-LSAs to signal that it is restarting to its + neighbors."; + } + + leaf helper-only { + type boolean; + description + "Operate graceful-restart only in helper mode. When this leaf + is set to true, the local system does not use Grace-LSAs to + indicate that it is restarting, but will accept Grace-LSAs + from remote systems, and suppress withdrawl of adjacencies + of the system for the grace period specified"; + } + } + + grouping ospfv2-global-mpls-config { + description + "Configuration parameters for OSPFv2 options which + relate to MPLS"; + + leaf traffic-engineering-extensions { + type boolean; + description + "When this leaf is set to true, use traffic engineering + extensions for OSPF to advertise TE parameters via type 10 + Opaque LSAs"; + } + } + + grouping ospfv2-global-inter-areapp-config { + description + "Configuration parameters for OSPFv2 policies which propagate + prefixes between areas"; + + leaf src-area { + type leafref { + // we are at ospf/global/inter-area-propagation-policies/... + // inter-area-propagation-policy/config/src-area + path "../../../../../areas/area/identifier"; + } + description + "The area from which prefixes are to be exported."; + } + + leaf dst-area { + type leafref { + // we are at ospf/global/inter-area-propagation-policies/... + // inter-area-propagation-policy/config/src-area + path "../../../../../areas/area/identifier"; + } + description + "The destination area to which prefixes are to be imported"; + } + + uses oc-rpol:apply-policy-import-config; + } + + grouping ospfv2-global-max-metric-config { + description + "Configuration paramters relating to setting the OSPFv2 + maximum metric."; + + leaf set { + type boolean; + description + "When this leaf is set to true, all non-stub interfaces of + the local system are advertised with the maximum metric, + such that the router does not act as a transit system, + (similarly to the IS-IS overload functionality)."; + reference + "RFC3137 - OSPF Stub Router Advertisement"; + } + + leaf timeout { + type uint64; + units "seconds"; + description + "The delay, in seconds, after which the advertisement of + entities with the maximum metric should be cleared, and + the system reverts to the default, or configured, metrics."; + } + + leaf-list include { + type identityref { + base "oc-ospft:MAX_METRIC_INCLUDE"; + } + description + "By default, the maximum metric is advertised for all + non-stub interfaces of a device. When identities are + specified within this leaf-list, additional entities + are also advertised with the maximum metric according + to the values within the list."; + } + + leaf-list trigger { + type identityref { + base "oc-ospft:MAX_METRIC_TRIGGER"; + } + description + "By default, the maximum metric is only advertised + when the max-metric/set leaf is specified as true. + In the case that identities are specified within this + list, they provide additional triggers (e.g., system + boot) that may cause the max-metric to be set. In this + case, the system should still honour the timeout specified + by the max-metric/timeout leaf, and clear the max-metric + advertisements after the expiration of this timer."; + } + } + + grouping ospfv2-global-structural { + description + "Top level structural grouping for OSPFv2 global parameters"; + + container global { + description + "Configuration and operational state parameters for settings + that are global to the OSPFv2 instance"; + + container config { + description + "Global configuration parameters for OSPFv2"; + uses ospfv2-global-config; + } + + container state { + config false; + description + "Operational state parameters for OSPFv2"; + uses ospfv2-global-config; + } + + container timers { + description + "Configuration and operational state parameters for OSPFv2 + timers"; + + container spf { + description + "Configuration and operational state parameters relating + to timers governing the operation of SPF runs"; + + container config { + description + "Configuration parameters relating to global OSPFv2 + SPF timers"; + uses ospfv2-global-spf-timers-config; + } + + container state { + config false; + description + "Operational state parameters relating to the global + OSPFv2 SPF timers"; + uses ospfv2-global-spf-timers-config; + uses ospfv2-global-spf-timers-state; + } + } + + container max-metric { + description + "Configuration and operational state parameters relating + to setting the OSPFv2 maximum metric."; + + container config { + description + "Configuration parameters relating to setting the OSPFv2 + maximum metric for a set of advertised entities."; + uses ospfv2-global-max-metric-config; + } + + container state { + config false; + description + "Operational state parameters relating to setting the + OSPFv2 maximum metric for a set of advertised entities."; + uses ospfv2-global-max-metric-config; + } + } + + container lsa-generation { + description + "Configuration and operational state parameters relating + to timers governing the generation of LSAs by the local + system"; + + container config { + description + "Configuration parameters relating to the generation of + LSAs by the local system"; + uses ospfv2-global-lsa-generation-timers-config; + } + + container state { + config false; + description + "Operational state parameters relating to the generation + of LSAs by the local system"; + uses ospfv2-global-lsa-generation-timers-config; + uses ospfv2-global-lsa-generation-timers-state; + } + } + } + + container graceful-restart { + description + "Configuration and operational state parameters for OSPFv2 + graceful restart"; + + container config { + description + "Configuration parameters relating to OSPFv2 graceful + restart"; + uses ospfv2-global-graceful-restart-config; + } + + container state { + config false; + description + "Operational state parameters relating to OSPFv2 graceful + restart"; + uses ospfv2-global-graceful-restart-config; + } + } + + container mpls { + description + "OSPFv2 parameters relating to MPLS"; + + container config { + description + "Configuration parameters relating to MPLS for OSPFv2"; + uses ospfv2-global-mpls-config; + } + + container state { + config false; + description + "Operational state parameters relating to MPLS for + OSPFv2"; + uses ospfv2-global-mpls-config; + } + + container igp-ldp-sync { + description + "OSPFv2 parameters relating to LDP/IGP synchronization"; + + container config { + description + "Configuration parameters relating to LDP/IG + synchronization."; + uses ospfv2-common-mpls-igp-ldp-sync-config; + } + + container state { + config false; + description + "Operational state variables relating to LDP/IGP + synchronization"; + uses ospfv2-common-mpls-igp-ldp-sync-config; + } + } + } + + container inter-area-propagation-policies { + description + "Policies defining how inter-area propagation should be performed + by the OSPF instance"; + + list inter-area-propagation-policy { + key "src-area dst-area"; + description + "A list of connections between pairs of areas - routes are + propagated from the source (src) area to the destination (dst) + area according to the policy specified"; + + leaf src-area { + type leafref { + path "../config/src-area"; + } + description + "Reference to the source area"; + } + + leaf dst-area { + type leafref { + path "../config/dst-area"; + } + description + "Reference to the destination area"; + } + + container config { + description + "Configuration parameters relating to the inter-area + propagation policy"; + uses ospfv2-global-inter-areapp-config; + } + + container state { + config false; + description + "Operational state parameters relating to the inter-area + propagation policy"; + uses ospfv2-global-inter-areapp-config; + } + } + } + } + } +} \ No newline at end of file diff --git a/models/yang/common/openconfig-ospfv2-lsdb.yang b/models/yang/common/openconfig-ospfv2-lsdb.yang new file mode 100644 index 0000000000..61237bbeac --- /dev/null +++ b/models/yang/common/openconfig-ospfv2-lsdb.yang @@ -0,0 +1,2361 @@ +submodule openconfig-ospfv2-lsdb { + + belongs-to openconfig-ospfv2 { + prefix "oc-ospfv2"; + } + + // import some basic types + import ietf-yang-types { prefix "yang"; } + import ietf-inet-types { prefix "inet"; } + import openconfig-types { prefix "oc-types"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-ospf-types { prefix "oc-ospf-types"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "An OpenConfig model for the Open Shortest Path First (OSPF) + version 2 link-state database (LSDB)"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping ospfv2-lsdb-common-prefix-properties { + description + "Common properties used in the LSDB that relate to IPv4 prefixes"; + + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the IPv4 prefix contained in the Extended Prefix LSA"; + } + + leaf address-family { + // TODO: should this be an identity? + type enumeration { + enum IPV4_UNICAST { + value 0; + description + "The prefix contained within the Extended Prefix LSA is an IPv4 + unicast prefix"; + } + } + description + "The address family of the prefix contained in the Extended Prefix + LSA"; + } + } + + grouping ospfv2-lsdb-common-link-specification { + description + "Generic attributes used to identify links within OSPFv2"; + + leaf link-id { + type yang:dotted-quad; + description + "The identifier for the link specified. The value of the link + identifier is dependent upon the type of the LSA. The value is + specified to be, per sub-type: + 1) Neighbouring router's router ID. + 2) IP address of DR. + 3) IP network address. + 4) Neighbouring router router's ID."; + } + + leaf link-data { + type union { + type yang:dotted-quad; + type uint32; + } + description + "The data associated with the link type. The value is + dependent upon the subtype of the LSA. When the connection is + to a stub network it represents the mask; for p2p connections + that are unnumbered it represents the ifIndex value of the + router's interface; for all other connections it represents + the local system's IP address"; + } + + } + + grouping ospfv2-lsdb-common-unknown-tlv { + description + "A generic specification of a TLV to be used when the + value cannot be decoded by the local system"; + + leaf type { + type uint16; + description + "The type value of the unknown TLV"; + } + + leaf length { + type uint16; + description + "The length value of the unknown TLV"; + } + + leaf value { + type binary; + description + "The value portion of the unknwon TLV"; + } + } + + grouping ospfv2-lsdb-common-unknown-tlv-structure { + description + "A generic specification of an unknown TLV"; + + container unknown-tlv { + description + "An unknown TLV within the context. Unknown TLVs are + defined to be the set of TLVs that are not modelled + within the OpenConfig model, or are unknown to the + local system such that it cannot decode their value."; + + container state { + description + "Contents of an unknown TLV within the LSA"; + uses ospfv2-lsdb-common-unknown-tlv; + } + } + } + + grouping ospfv2-lsdb-common-unknown-subtlv-structure { + description + "A generic specification of an unknown TLV"; + + container unknown-subtlv { + description + "An unknown SubTLV within the context. Unknown Sub-TLV + are defined to be the set of SubTLVs that are not modelled + by the OpenConfig schema, or are unknown to the local system + such that it cannot decode their value."; + + container state { + description + "Contents of an unknown TLV within the LSA"; + uses ospfv2-lsdb-common-unknown-tlv; + } + } + } + + grouping ospfv2-lsdb-common-tos-metric { + description + "Common LSDB LSA parameters for type of service and metric"; + + leaf tos { + type uint8; + description + "OSPF encoding of the type of service referred to by this + LSA. Encoding for OSPF TOS are described in RFC2328."; + } + + leaf metric { + type oc-ospf-types:ospf-metric; + description + "The metric value to be used for the TOS specified. This value + represents the cost of use of the link for the specific type + of service."; + } + } + + grouping ospfv2-lsdb-common-sr-sid-spec { + description + "Re-usable specification of a segment routing SID"; + + leaf sid-type { + type oc-ospf-types:sr-sid-type; + description + "The type of the value contained within the sub-TLV"; + } + + leaf sid-value { + type uint32; + description + "The value of the binding included within the sub-TLV. The type of + this binding is indicated by the type leaf."; + } + } + + grouping ospfv2-lsdb-area-state { + description + "Per-area operational state parameters for an OSPFv2 area"; + + leaf identifier { + type oc-ospf-types:ospf-area-identifier; + description + "An identifier for the area, expressed as a dotted quad or + an unsigned 32-bit integer"; + } + } + + grouping ospfv2-lsdb-area-lsa-type-state { + description + "Per-LSA type operational state parameters for an OSPFv2 area"; + + leaf type { + type identityref { + base "oc-ospf-types:OSPF_LSA_TYPE"; + } + description + "The type of LSA being described. The type of the LSA is + expressed as a canonical name."; + } + } + + grouping ospfv2-lsdb-area-lsa-state { + description + "Generic parameters of an OSPFv2 LSA"; + + leaf link-state-id { + type yang:dotted-quad; + description + "The Link State ID for the specified LSA type. The exact + defined value of the Link State ID is dependent on the LSA + type."; + } + + leaf advertising-router { + type yang:dotted-quad; + description + "The router ID of the router that originated the LSA"; + } + + leaf sequence-number { + type int32; + description + "A signed 32-bit integer used to detect old and duplicate + LSAs. The greater the sequence number the more recent the + LSA."; + } + + leaf checksum { + type uint16; + description + "The checksum of the complete contents of the LSA excluding + the age field."; + } + + leaf age { + type uint16; + units seconds; + description + "The time since the LSA's generation in seconds"; + } + } + + grouping ospfv2-lsdb-router-lsa-structure { + description + "Structural grouping for Router LSA contents within the LSDB"; + + container router-lsa { + description + "Contents of the router LSA"; + + container state { + description + "State parameters of the router LSA"; + uses ospfv2-lsdb-router-lsa-state; + } + + uses ospfv2-lsdb-generic-lsa-tos-metric-structure; + } + } + + grouping ospfv2-lsdb-generic-lsa-tos-metric-structure { + description + "Grouping including a generic TOS/metric structure for an + LSA"; + + container types-of-service { + description + "Breakdown of LSA contents specifying multiple + TOS values"; + + list type-of-service { + key "tos"; + description + "Per-type of service parameters for the LSA"; + + leaf tos { + type leafref { + path "../state/tos"; + } + description + "Reference to the type of service identifier which is + specified in the LSA"; + } + + container state { + description + "Per-TOS parameters for the LSA"; + + uses ospfv2-lsdb-generic-lsa-tos-metric-state; + } + } + } + } + + grouping ospfv2-lsdb-network-lsa-structure { + description + "Structural grouping for Network LSA contents within the LSDB"; + + container network-lsa { + description + "Contents of the network LSA"; + + container state { + description + "State parameters of the network LSA"; + uses ospfv2-lsdb-network-lsa-state; + } + } + } + + grouping ospfv2-lsdb-summary-lsa-structure { + description + "Structural grouping for the Summary LSA contents within the + LSDB"; + + container summary-lsa { + description + "Contents of the summary LSA"; + + container state { + description + "State parameters of the summary LSA"; + uses ospfv2-lsdb-summary-lsa-state; + } + + uses ospfv2-lsdb-generic-lsa-tos-metric-structure; + } + } + + grouping ospfv2-lsdb-asexternal-lsa-structure { + description + "Structural grouping for the AS External LSA contents within + the LSDB"; + + container as-external-lsa { + description + "Contents of the AS External LSA"; + + container state { + description + "State parameters for the AS external LSA"; + uses ospfv2-lsdb-asexternal-lsa-state; + } + + container types-of-service { + description + "Breakdown of External LSA contents specifying multiple + TOS values"; + + list type-of-service { + key "tos"; + description + "Per-type of service parameters for the AS External LSA"; + + leaf tos { + type leafref { + path "../state/tos"; + } + description + "Reference to the type of service identifier which is + specified in the AS External LSA"; + } + + container state { + description + "Per-TOS parameters for the LSA"; + + uses ospfv2-lsdb-asexternal-tos-state; + } + } + } + + } + } + + grouping ospfv2-lsdb-nssa-external-lsa-structure { + description + "Structural grouping for the NSSA External LSA contents within + the LSDB"; + + container nssa-external-lsa { + description + "Contents of the NSSA External LSA"; + + container state { + description + "State parameters for the AS external LSA"; + // Type 7 LSAs are are a super-set of Type 5 LSAs so we simply + // include the Type 5 + uses ospfv2-lsdb-asexternal-lsa-state; + uses ospfv2-lsdb-nssa-external-lsa-state; + } + + container types-of-service { + description + "Breakdown of the NSSA External LSA contents specifying multiple + TOS values"; + + list type-of-service { + key "tos"; + description + "Per-type of service parameters for the NSSA external LSA"; + + leaf tos { + type leafref { + path "../state/tos"; + } + description + "Reference to the type of services identifier which is specified + in the NSSA External LSA"; + } + + container state { + description + "Per-TOS parameters for the LSA"; + uses ospfv2-lsdb-asexternal-tos-state; + } + } + } + } + } + + grouping ospfv2-lsdb-opaque-lsa-structure { + description + "Structural grouping for Opaque LSA contents within the LSDB"; + + container opaque-lsa { + description + "Contents of the opaque LSA"; + + container state { + description + "State parameters for the opaque LSA"; + uses ospfv2-lsdb-opaque-lsa-state; + } + + container traffic-engineering { + when "../state/type = 'TRAFFIC_ENGINEERING'" { + description + "Include the traffic-engineering information when + the Opaque LSA being described is a Traffic Engineering + LSA"; + } + description + "Contents of the Traffic Engineering Opaque LSA"; + + container tlvs { + description + "The TLVs contained in the TE Opaque LSA"; + list tlv { + // this is an unkeyed list + description + "The Type-Length-Value tuples included in the TE LSA"; + + container state { + description + "The contents of the TLV tuple within the TE LSA"; + uses ospfv2-lsdb-opaque-lsa-te-tlv-state; + } + + uses ospfv2-lsdb-common-unknown-tlv-structure; + + container router-address { + when "../state/type = 'TE_ROUTER_ADDRESS'" { + description + "Include the router address container only when the type + of the TLV is Router Address"; + } + + description + "Parameters included in the Router Address TLV"; + + container state { + description + "State parameters of the router address TLV"; + uses ospfv2-lsdb-opaque-te-router-address-state; + } + } + + container link { + when "../state/type = 'TE_ROUTER_LINK'" { + description + "Include the link container only when the type of the + TLV describes a traffic engineering link"; + } + + description + "Parameters included in the Link TLV"; + container sub-tlvs { + description + "Sub-TLVs included in the Link TLV"; + + list sub-tlv { + // unkeyed list + description + "The Sub-TLVs included within the Traffic Engineering + LSA's sub-TLV"; + + container state { + description + "State parameters of the Link Sub-TLV"; + + uses ospfv2-lsdb-opaque-te-link-state; + } + + uses ospfv2-lsdb-common-unknown-subtlv-structure; + + container unreserved-bandwidths { + description + "The unreserved link bandwidths for the Traffic + Engineering LSA - utilised when the sub-TLV type + indicates that the sub-TLV describes unreserved + bandwidth"; + + list unreserved-bandwidth { + key "priority"; + + description + "The unreserved bandwidth at each priority level"; + + leaf priority { + type leafref { + path "../state/priority"; + } + description + "A reference to the priority level being described"; + } + + container state { + description + "State parameters relating to the unreserved + bandwidth of the link being described"; + uses ospfv2-lsdb-opaque-te-link-unreserved-bw-state; + } + } + } + + container administrative-groups { + description + "The administrative groups that are set for the + Traffic Engineering LSA - utilised when the sub-TLV type + indicates that the sub-TLV describes administrative + groups"; + + list admin-group { + key "bit-index"; + + description + "The administrative group described within the + sub-TLV"; + + leaf bit-index { + type leafref { + path "../state/bit-index"; + } + description + "A reference to the bit index being described"; + } + + container state { + description + "State parameters relating to the administrative + groups being described for the link"; + uses ospfv2-lsdb-opaque-te-link-admin-group-state; + } + } + } + } + } + } + + container node-attribute { + when "../state/type = 'TE_NODE_ATTRIBUTE'" { + description + "Include the node-attribute container only when the type of + the TLV describes a node attribute"; + } + + description + "Parameters included in the Node Attribute TLV"; + + container sub-tlvs { + description + "Sub-TLVs of the Node Attribute TLV of the Traffic + Engineering LSA"; + + list sub-tlv { + // unkeyed list + description + "List of the Sub-TLVs contained within the Node Attribute + TLV"; + + container state { + description + "State parameters of the Node Attribute TLV sub-TLV"; + uses ospfv2-lsdb-opaque-te-node-attribute-state; + } + + uses ospfv2-lsdb-common-unknown-subtlv-structure; + } + } + } + + // A set of TLVs are omitted here - based on operational + // requirements, these are: + // * link-local + // * ipv6-address (OSPFv3 only) + // * optical-node-property + } + } + } // traffic-engineering + + container grace-lsa { + when "../state/type = 'GRACE_LSA'" { + description + "Include the grace-lsa container when the opaque LSA is specified + to be of that type."; + } + + description + "The Grace LSA is utilised when a remote system is undergoing + graceful restart"; + + container tlvs { + description + "TLVs of the Grace LSA"; + + list tlv { + description + "TLV entry in the Grace LSA, advertised by a system undergoing + graceful restart"; + + // unkeyed list + container state { + description + "Per-TLV state parameters of the Grace LSA"; + uses ospfv2-lsdb-opaque-grace-state; + } + uses ospfv2-lsdb-common-unknown-tlv-structure; + } + } + } // grace LSA + + container router-information { + when "../state/type = 'ROUTER_INFORMATION_LSA'" { + description + "Include the router-information container when the opaque LSA + type is specified to be an RI LSA"; + } + + description + "The router information LSA is utilised to advertise capabilities + of a system to other systems who receive the LSA"; + + container tlvs { + description + "The TLVs included within the Router Information LSA."; + + list tlv { + description + "TLV entry in the Router Information LSA"; + + // unkeyed list + container state { + description + "Per-TLV state parameters of the RI LSA"; + uses ospfv2-lsdb-opaque-ri-state; + } + + uses ospfv2-lsdb-common-unknown-tlv-structure; + + container informational-capabilities { + when "../state/type = 'RI_INFORMATIONAL_CAPABILITIES'" { + description + "Include the informational capabilities specification when + the TLV of the RI LSA is specified to be of this type"; + } + + description + "Information related to the capabilities of the advertising + router within the scope that the opaque RI LSA is being + advertised"; + + container state { + description + "State parameters of the informational capabilitis of the + RI LSA"; + uses ospfv2-lsdb-opaque-ri-informational-state; + } + } + + container node-administrative-tags { + when "../state/type = 'RI_NODE_ADMIN_TAG'" { + description + "Include the node administrative tags specification when + the TLV of the RI LSA is specified to be of this type"; + } + + description + "Per-node administrative tags associated with the local system + specified by the operator"; + + container state { + description + "State parameters of the node administrative tags advertised + in the RI LSA"; + uses ospfv2-lsdb-opaque-ri-admintag-state; + } + } + + container segment-routing-algorithm { + when "../state/type = 'RI_SR_ALGORITHM'" { + description + "Include the segment routing algorithm specific parameters when + the TLV of the RI LSA is specified to be of this type"; + } + + description + "The algorithms supported for Segment Routing by the local system"; + + container state { + description + "State parameters of the Segment Routing algorithm advertised in + the RI LSA"; + uses ospfv2-lsdb-opaque-ri-sralgo-state; + } + } + + container segment-routing-sid-label-range { + when "../state/type = 'RI_SR_SID_LABEL_RANGE'" { + description + "Include the segment routing SID/Label range TLV specific state when + the TLV of the RI LSA is specified to be of this type"; + } + + description + "The Segment Identifier (SID) or label ranges that are supported by + the local system for Segment Routing"; + + container tlvs { + description + "Sub-TLVs of the SID/Label range TLV of the RI LSA"; + + list tlv { + // unkeyed list + description + "Sub-TLVs of the SID/Label range TLV"; + + uses ospfv2-lsdb-common-unknown-tlv-structure; + + container state { + description + "State parameters of the sub-TLVs of the SR/Label range TLV"; + uses ospfv2-lsdb-opaque-ri-srrange-tlv-state; + } + + container sid-label { + description + "Sub-TLV used to advertise the SID or label associated with the + subset of the SRGB being advertised"; + + container state { + description + "State parameters of the SID/Label sub-TLV of the SR/Label + range TLV of the RI LSA"; + uses ospfv2-lsdb-opaque-ri-srrange-sid-label-tlv-state; + } + } + } + } + } + } + } + } // router-information + + container extended-prefix { + when "../state/type = 'OSPFV2_EXTENDED_PREFIX'" { + description + "Include the extended-prefix container when the opaque LSA + type is specified to be an extended prefix LSA"; + } + + description + "An OSPFv2 Extended Prefix Opaque LSA, used to encapsulate + TLV attributes associated with a prefix advertised in OSPF."; + + reference "RFC7684 - OSPFv2 Prefix/Link Attribute Advertisement"; + + container state { + description + "State parameters of the Extended Prefix LSA"; + uses ospfv2-lsdb-extended-prefix-state; + } + + container tlvs { + description + "TLVs contained within the Extended Prefix LSA"; + + list tlv { + // unkeyed list + description + "A TLV contained within the extended prefix LSA"; + + container state { + description + "State parameters relating to the sub-TLV of the extended + prefix LSA"; + uses ospfv2-lsdb-extended-prefix-tlv-state; + } + + container extended-prefix-range { + when "../state/type = 'EXTENDED_PREFIX_RANGE'" { + description + "Include the prefix range sub-TLV when the type of the + sub-TLV is specified as such"; + } + + description + "State parameters relating to the extended prefix range + sub-TLV of the extended prefix LSA"; + + container state { + description + "State parameters relating to the Extended Prefix Range + sub-TLV of the Extended Prefix LSA"; + uses ospfv2-lsdb-extended-prefix-range-state; + } + } + + container prefix-sid { + when "../state/type = 'PREFIX_SID'" { + description + "Include parameters relating to the Prefix SID when the type + of the sub-TLV is indicated as such"; + } + + description + "State parameters relating to the Prefix SID sub-TLV of the + extended prefix LSA"; + + container state { + description + "State parameters relating to the Prefix SID sub-TLV of the + extended prefix LSA"; + uses ospfv2-lsdb-extended-prefix-prefix-sid-state; + } + } // prefix-sid + + container sid-label-binding { + when "../state/type = 'SID_LABEL_BINDING'" { + description + "Include parameters relating to the SID/Label binding sub-TLV + only when the type is indicated as such"; + } + + description + "State parameters relating to the SID/Label binding sub-TLV + of the extended prefix LSA"; + + container state { + description + "State parameters relating to the SID/Label binding sub-TLV + of the extended prefix LSA"; + uses ospfv2-lsdb-extended-prefix-sid-label-binding-state; + } + + container tlvs { + description + "TLVs contained within the SID/Label Binding sub-TLV of the + SID/Label Binding TLV"; + + list tlv { + description + "A TLV contained within the SID/Label Binding sub-TLV"; + + container state { + description + "State parameters relating to the SID/Label Binding + sub-TLV"; + uses ospfv2-lsdb-extended-prefix-sid-label-binding-tlv-state; + } + + container sid-label-binding { + when "../state/type = 'SID_LABEL_BINDING'" { + description + "Include the SID/Label Binding sub-TLV parameters only + when the type is indicated as such"; + } + + description + "Parameters for the SID/Label Binding sub-TLV of the + SID/Label binding TLV"; + + container state { + description + "State parameteres relating to the SID/Label Binding + sub-TLV"; + uses ospfv2-lsdb-extprefix-sid-label-binding-state; + } + } // sid-label-binding + + container ero-metric { + when "../state/type = 'ERO_METRIC'" { + description + "Include the ERO Metric sub-TLV parameters only when + the type is indicated as such"; + } + + description + "Parameters for the ERO Metric Sub-TLV of the SID/Label + binding TLV"; + + container state { + description + "State parameters relating to the ERO Metric Sub-TLV of + the SID/Label binding TLV"; + uses ospfv2-lsdb-extprefix-sid-label-ero-metric-state; + } + } // ero-metric + + container ero-path { + when "../state/type = 'ERO_PATH'" { + description + "Include the ERO Path sub-TLV parameters only when the + type is indicated as such"; + } + + description + "Parameters for the ERO Path Sub-TLV of the SID/Label + binding TLV"; + + container segments { + description + "Segments of the path described within the SID/Label + Binding sub-TLV"; + + list segment { + description + "A segment of the path described within the sub-TLV"; + + container state { + description + "State parameters relating to the path segment + contained within the sub-TLV"; + uses ospfv2-lsdb-extprefix-sid-lbl-ero-path-seg-state; + } + + container ipv4-segment { + when "../state/type = 'IPV4_SEGMENT'" { + description + "Include the IPv4 segment only when the type is + indicated as such"; + } + + description + "Details of the IPv4 segment interface of the ERO"; + + container state { + description + "State parameters of the IPv4 segment of the ERO"; + uses ospfv2-lsdb-extprefix-sid-lbl-ero-ipv4-state; + } + } // ipv4-segment + + container unnumbered-hop { + when "../state/type = 'UNNUMBERED_INTERFACE_SEGMENT'" { + description + "Include the unnumbered segment only when the + type is indicated as such"; + } + + description + "Details of the unnumbered interface segment of the + ERO"; + + container state { + description + "State parameters of the unnumbered interface + segment of the ERO"; + uses ospfv2-lsdb-extprefix-sid-lbl-ero-unnum-state; + } + } // unnumbered-hop + } // tlv + } // tlvs + } + } + } + } // sid-label-binding + + uses ospfv2-lsdb-common-unknown-tlv-structure; + } + } + } // extended-prefix + + container extended-link { + description + "The OSPFv2 Extended Link Opaque LSA, used to encapsulate TLV + attributes associated with a link advertised in OSPF."; + + reference "RFC7684 - OSPFv2 Prefix/Link Attribute Advertisement"; + + container state { + description + "State parameters of the Extended Link LSA"; + uses ospfv2-lsdb-extended-link-state; + } + + container tlvs { + description + "TLVs contained within the Extended Link LSA"; + + list tlv { + description + "List of TLVs within the Extended Link LSA"; + + container state { + description + "State parameters relating to the sub-TLV of the extended link + LSA"; + uses ospfv2-lsdb-extended-link-tlv-state; + } + + uses ospfv2-lsdb-common-unknown-tlv-structure; + + container adjacency-sid { + when "../state/type = 'ADJACENCY_SID'" { + description + "Include the Adjacency SID parameters only when the type of + the sub-TLV is indicated as such"; + } + + description + "Parameters relating to an Adjacency SID sub-TLV of the + extended link LSA"; + + container state { + description + "State parameters relating to an Adjacency SID"; + + uses ospfv2-lsdb-extended-link-adj-sid-state; + } + } + } + } + + } // extended-link + + uses ospfv2-lsdb-common-unknown-tlv-structure; + } + } + + grouping ospfv2-lsdb-generic-lsa-tos-metric-state { + description + "Per-TOS state parameters for the Router LSA"; + + uses ospfv2-lsdb-common-tos-metric; + } + + grouping ospfv2-lsdb-router-lsa-state { + description + "Parameters of the router LSA"; + + leaf type { + type identityref { + base "oc-ospf-types:ROUTER_LSA_TYPES"; + } + description + "The sub-type of the Router LSA."; + } + + uses ospfv2-lsdb-common-link-specification; + + leaf metric { + type oc-ospf-types:ospf-metric; + description + "The cost of utilising the link specified independent of TOS"; + } + + leaf number-links { + type uint16; + description + "The number of links that are described within the LSA"; + } + + leaf number-tos-metrics { + type uint16; + description + "The number of different TOS metrics given for this link, not + including the link metric (which is referred to as TOS 0)."; + } + } + + grouping ospfv2-lsdb-network-lsa-state { + description + "Parameters of the Network LSA"; + + leaf network-mask { + type uint8 { + range "0..32"; + } + description + "The mask of the network described by the Network LSA + represented as a CIDR mask."; + } + + leaf-list attached-router { + type yang:dotted-quad; + description + "A list of the router ID of the routers that are attached to + the network described by the Network LSA"; + } + } + + grouping ospfv2-lsdb-summary-lsa-state { + description + "Parameters of the Summary LSA"; + + leaf network-mask { + type uint8 { + range "0..32"; + } + description + "The mask of the network described by the Summary LSA + represented as a CIDR mask."; + } + } + + grouping ospfv2-lsdb-asexternal-lsa-common-parameters { + description + "Common parameters that are used for OSPFv2 AS External LSAs"; + + leaf forwarding-address { + type inet:ipv4-address-no-zone; + description + "The destination to which traffic for the external prefix + should be advertised. When this value is set to 0.0.0.0 then + traffic should be forwarded to the LSA's originator"; + } + + leaf external-route-tag { + type uint32; + description + "An opaque tag that set by the LSA originator to carry + information relating to the external route"; + } + } + + grouping ospfv2-lsdb-asexternal-lsa-state { + description + "Parameters for the AS External LSA"; + + leaf mask { + type uint8 { + range "0..32"; + } + description + "The subnet mask for the advertised destination"; + } + + leaf metric-type { + type enumeration { + enum "TYPE_1" { + description + "When the metric of a prefix is specified as Type 1 + then it is considered to be expressed in the same units as + the link-state metrics carried in OSPF. That is to say + that the metric advertised is directly compared to the + internal cost"; + } + enum "TYPE_2" { + description + "When the metric of a prefix is specified as Type 2 then + it is considered to be expressed as a cost in addition to + that of the link-state metric to the advertising router. + That is to say that the metric is considered to be the + cost to the advertising router plus the advertised metric + for the external entity"; + } + } + description + "The type of metric included within the AS External LSA."; + } + + leaf metric { + type oc-ospf-types:ospf-metric; + description + "The cost to reach the external network specified. The exact + interpretation of this cost is dependent on the type of + metric specified"; + } + + uses ospfv2-lsdb-asexternal-lsa-common-parameters; + } + + grouping ospfv2-lsdb-asexternal-tos-state { + description + "Per-TOS parameters for the AS External LSA"; + + uses ospfv2-lsdb-asexternal-lsa-common-parameters; + uses ospfv2-lsdb-common-tos-metric; + } + + grouping ospfv2-lsdb-nssa-external-lsa-state { + description + "Operational state parameters specific to the NSSA External + LSA"; + + leaf propagate { + type boolean; + description + "When this bit is set to true, an NSSA border router will + translate a Type 7 LSA (NSSA External) to a Type 5 LSA + (AS External)."; + reference "RFC3101, Section 2.3"; + } + } + + grouping ospfv2-lsdb-opaque-lsa-state { + description + "Operational state parameters specific to an Opaque LSA"; + + leaf scope { + type enumeration { + enum LINK { + description + "The scope of the LSA is the current link. The LSA + is not flooded beyond the local network. This + enumeration denotes a Type 9 LSA."; + } + enum AREA { + description + "The scope of the LSA is the local area. The LSA + is not flooded beyond the area of origin. This + enumeration denotes a Type 10 LSA."; + } + enum AS { + description + "The scope of the LSA is the local autonomous + system (AS). The flooding domain is the same + as a Type 5 LSA - it is not flooded into + stub areas or NSSAs. This enumeration denotes a + Type 11 LSA."; + } + } + description + "The scope of the opaque LSA. The type of the LSA + indicates its scope - the value of this leaf + determines both the flooding domain, and the type + of the LSA."; + } + + leaf type { + type identityref { + base "oc-ospf-types:OSPF_OPAQUE_LSA_TYPE"; + } + description + "The Opaque Type of the LSA. This value is used to + indicate the type of data carried by the opaque LSA"; + } + } + + grouping ospfv2-lsdb-opaque-lsa-te-tlv-state { + description + "The contents of the Traffic Engineering LSA"; + + leaf type { + type identityref { + base "oc-ospf-types:OSPF_TE_LSA_TLV_TYPE"; + } + description + "The type of TLV within the Traffic Engineering LSA"; + } + } + + grouping ospfv2-lsdb-opaque-te-unknown-state { + description + "The contents of the unknown TLV within the Traffic Engineering LSA"; + + uses ospfv2-lsdb-common-unknown-tlv; + } + + grouping ospfv2-lsdb-opaque-te-link-state { + description + "The contents of the sub-TLVs of a Traffic Engineering LSA Link TLV"; + + leaf type { + type union { + type identityref { + base "oc-ospf-types:OSPF_TE_LINK_TLV_TYPE"; + } + type enumeration { + enum UNKNOWN { + description + "The sub-TLV received in the LSA is unknown to the local + system"; + } + } + } + description + "The sub-TLV type specified in the Link TLV. When the value is + known by the local system, a canonical name of the sub-TLV is utilised + - the special UNKNOWN value indicates that the system did not + support the sub-TLV type received in the LSA."; + } + + leaf unknown-type { + when "../type = 'UNKNOWN'" { + description + "Include the unknown type field only when the sub-TLV was not + known to the local system"; + } + + type uint16; + description + "The value of the type field of an unknown sub-TLV"; + } + + leaf unknown-value { + when "../type = 'UNKNOWN'" { + description + "Include the unknown value field only when the sub-TLV was not + known to the local system"; + } + + type binary; + description + "The binary contents of the unknown TLV"; + } + + leaf link-type { + when "../type = 'TE_LINK_TYPE'" { + description + "Include the link-type field only when the sub-TLV type was a TE + link type"; + } + + type enumeration { + enum POINT_TO_POINT { + description + "The link being described by the TE LSA Link sub-TLV is a + point-to-point link to exactly one other system"; + } + enum MULTI_ACCESS { + description + "The link being described by the TE LSA Link sub-TLV is a + multi-access link that supports connectivity to multiple remote + systems"; + } + enum UNKNOWN { + description + "The link type received was unknown to the local system"; + } + } + description + "The type of the link that is being described by the TE LSA Link + sub-TLV"; + } + + leaf link-id { + when "../type = 'TE_LINK_ID'" { + description + "Include the link ID field only when the sub-TLV type was a TE + Link identifier"; + } + + type yang:dotted-quad; + description + "The ID of the remote system. For point-to-point links, this is the + router ID of the neighbor. For multi-access links it is the address + of the designated router."; + } + + leaf-list local-ip-address { + when "../type = 'TE_LINK_LOCAL_IP'" { + description + "Include the local IP address field only when the sub-TLV type was + a local IP address"; + } + + type inet:ipv4-address-no-zone; + description + "The IP address(es) of the local system that correspond to the + specified TE link"; + } + + leaf-list remote-ip-address { + when "../type = 'TE_LINK_REMOTE_IP'" { + description + "Include the remote IP address field only when the sub-TLV type was + a remote IP address"; + } + + type inet:ipv4-address-no-zone; + description + "The IP address(es) of the remote systems that are attached to the + specified TE link"; + } + + leaf metric { + when "../type = 'TE_LINK_METRIC'" { + description + "Include the traffic engineering metric only when the sub-TLV type + is a TE metric"; + } + + type uint32; + description + "The metric of the link that should be used for traffic engineering + purposes. This link may be different than the standard OSPF link + metric."; + } + + leaf maximum-bandwidth { + when "../type = 'TE_LINK_MAXIMUM_BANDWIDTH'" { + description + "Include the traffic engineering metric only when the sub-TLV type + is the maximum bandwidth"; + } + + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The maximum bandwidth of the link. This value reflects the actual + bandwidth of the link expressed asn IEEE 32-bit floating point + number"; + } + + leaf maximum-reservable-bandwidth { + when "../type = 'TE_LINK_MAXIUMUM_RESERVABLE_BANDWIDTH'" { + description + "Include the maximum reservable bandwidth field only when the + sub-TLV type is the maximum reservable bandwidth"; + } + + type oc-types:ieeefloat32; + units "bytes per second"; + description + "The maximum reservable bandwidth for the link. This value represents + the total bandwidth which may be used for traffic engineering + purposes. The value may exceed the maximum-bandwidth value + in cases where the link is oversubscribed. The value is reflected as + a 32-bit IEEE floating-point number"; + } + } + + grouping ospfv2-lsdb-opaque-te-link-unreserved-bw-state { + description + "The per-priority unreserved bandwidth described within the unreserved + bandwidth sub-TLV of the Link TLV of the Traffic Engineering LSA"; + + leaf priority { + type uint8 { + range "0..7"; + } + description + "The priority level being described"; + } + + leaf unreserved-bandwidth { + type oc-types:ieeefloat32; + description + "The unreserved bandwidth for at priority level P, where P is + equal to the priority of the current list entry. The reservable + bandwidth at priority P is equal to the sum of the reservable + bandwidth at all levels 0..P."; + } + } + + grouping ospfv2-lsdb-opaque-te-link-admin-group-state { + description + "Per bit administrative group status"; + + leaf bit-index { + type uint8 { + range "0..31"; + } + description + "The index of the bit within the 32-bit administrative group field + of the Administrative Group sub-TLV of the Traffic Engineering LSA"; + } + + leaf set { + type boolean; + default false; + description + "Whether the bit is set within the administrative group field"; + } + } + + grouping ospfv2-lsdb-opaque-te-node-attribute-state { + description + "State parameters relating to the Traffic Engineering Node Attribute + TLV of the Traffic Engineering LSA"; + + leaf type { + type union { + type identityref { + base "oc-ospf-types:TE_NODE_ATTRIBUTE_TLV_TYPE"; + } + type enumeration { + enum UNKNOWN { + description + "The sub-TLV type received within the TE LSA Node Attribute TLV + was unknown the the local system"; + } + } + } + description + "The type of the sub-TLV of the Node Attribute TLV contained within + the TE LSA. If the local system can interpret the value received the + canonical name of the type is utilised, otherwise the special UNKNOWN + value is used"; + } + + leaf-list local-ipv4-addresses { + when "../type = 'NODE_IPV4_LOCAL_ADDRESS'" { + description + "Include the local IPv4 addresses when the type of the sub-TLV + indicates that this is the contained data"; + } + + type inet:ipv4-prefix; + description + "The local IPv4 addresses of the node expressed in CIDR notation"; + } + + leaf-list local-ipv6-addresses { + when "../type = 'NODE_LOCAL_IPV6_ADDRESS'" { + description + "Include the local IPv6 addresses when the type of the sub-TLV + indicfates that this is the contained data"; + } + + type inet:ipv6-prefix; + description + "The local IPv6 addreses of the node"; + } + } + + grouping ospfv2-lsdb-opaque-te-router-address-state { + description + "The contents of the value field of the Router Address TLV of the + Traffic Engineering LSA."; + + leaf address { + type inet:ipv4-address-no-zone; + description + "A stable IP address of the advertising router, that is always + reachable when the router is connected to the network. Typically this + is a loopback address."; + } + } + + grouping ospfv2-lsdb-opaque-grace-state { + description + "State parameters on a per-TLV basis of the Grace LSA"; + + leaf type { + type identityref { + base "oc-ospf-types:GRACE_LSA_TLV_TYPES"; + } + description + "The type of the sub-TLV received within the Grace LSA"; + } + + leaf period { + when "../type = 'GRACE_PERIOD'" { + description + "Include the period specification when the sub-TLV type is indicated + to be of this type"; + } + + type uint32; + units seconds; + description + "The number of seconds that the router's neighbors should advertise + the local system as fully adjacent regardless of database + synchronization state"; + reference "RFC3623"; + } + + leaf reason { + when "../type = 'GRACE_RESTART_REASON'" { + description + "Include the restart reason when the sub-TLV type specifies this + is included"; + } + + type enumeration { + enum UNKNOWN { + value 0; + description + "The reason for the graceful restart is unknown"; + } + enum SOFTWARE_RESTART { + value 1; + description + "The local system is restarting due to a software component + restart"; + } + enum SOFTWARE_RELOAD_UPGRADE { + value 2; + description + "The local system is restarting due to a software reload or + upgrade"; + } + enum CONTROL_PROCESSOR_SWITCH { + value 3; + description + "The local system is restarting due to a switch to a redundant + control plane element"; + } + } + description + "The reason for the graceful restart event occurring, as advertised + by the restarting system"; + reference "RFC3623"; + } + + leaf ip-interface-address { + when "../type = 'GRACE_IP_INTERFACE_ADDRESS'" { + description + "Include the interface address when the sub-TLV type specifies that + it is included"; + } + + type inet:ipv4-address-no-zone; + description + "The restarting system's IP address on the interface via which the + Grace LSA is being advertised."; + } + } + + grouping ospfv2-lsdb-opaque-ri-state { + description + "State parameters of the Router Information Opaque LSA"; + + leaf type { + type union { + type identityref { + base "oc-ospf-types:RI_LSA_TLV_TYPES"; + } + type enumeration { + enum UNKNOWN { + description + "The TLV received within the RI LSA is unknown"; + } + } + } + description + "The type of sub-TLV of the Router Information opaque LSA"; + } + } + + grouping ospfv2-lsdb-opaque-ri-informational-state { + description + "State parmaeters of the Router Information Informational Capabilities + sub-TLV"; + + leaf graceful-restart-capable { + type boolean; + default false; + description + "When this leaf is set to true, the advertising system is capable of + OSPF graceful restart."; + } + + leaf graceful-restart-helper { + type boolean; + default false; + description + "When this leaf is set to true, the advertising system is capable of + being a helper for OSPF graceful restart"; + } + + leaf stub-router { + type boolean; + default false; + description + "When this leaf is set to true, the advertising system is able to + advertise its status as a stub router"; + reference "RFC6987"; + } + + leaf traffic-engineering { + type boolean; + default false; + description + "When this leaf is set to true, the advertising system supports OSPFv2 + traffic engineering capabilities"; + } + + leaf point-to-point-over-lan { + type boolean; + default false; + description + "When this leaf is set to true, the advertising system supports treating + LAN adjacencies as though they were point to point"; + reference "RFC5309"; + } + + leaf experimental-te { + type boolean; + default false; + description + "When this leaf is set to ture, the advertising system supports the + experimental extensions to OSPF for TE described in RFC4973"; + reference "RFC4973"; + } + } + + grouping ospfv2-lsdb-opaque-ri-admintag-state { + description + "State parameters relating to the administrative tags specified for + a node within the RI LSA"; + + leaf-list administrative-tags { + type uint32; + description + "The set of administrative tags assigned to the local system by + the network operator. The meaning of these tags is opaque to OSPF + - and their interpretation is per-domain specific"; + reference "RFC7777"; + } + } + + grouping ospfv2-lsdb-opaque-ri-unknown-state { + description + "State parameters relating to an unknown TLV within the RI LSA"; + uses ospfv2-lsdb-common-unknown-tlv; + } + + grouping ospfv2-lsdb-opaque-ri-sralgo-state { + description + "State parameters relating to the SR Algorithms TLV of the RI LSA"; + + leaf-list supported-algorithms { + type identityref { + base "oc-ospf-types:SR_ALGORITHM"; + } + description + "A list of the algorithms that are supported for segment routing + by the advertising system"; + } + } + + grouping ospfv2-lsdb-opaque-ri-srrange-tlv-state { + description + "State parameters relating to the SR SID/Label range TLV of the + RI LSA"; + + leaf type { + type union { + type identityref { + base "oc-ospf-types:OSPF_RI_SR_SID_LABEL_TLV_TYPES"; + } + type enumeration { + enum UNKNOWN { + description + "The type of the sub-TLV advertised with the SID/Label range + TLV of the RI LSA is unknown to the receiving system"; + } + } + } + description + "The type of the sub-TLV received by the local system within the + SR SID/Label Range TLV of the RI LSA"; + } + + leaf range-size { + type uint32 { + range "0..16777216"; + } + description + "The number of entries within the range being described within the + SID/Label range TLV"; + } + } + + grouping ospfv2-lsdb-opaque-ri-srrange-sid-label-tlv-state { + description + "State parameters relating to the SR SID/Label sub-TLV of the SR SID/Label + range TLV of the RI LSA"; + + leaf entry-type { + type oc-ospf-types:sr-sid-type; + description + "The type of entry that is contained within the sub-TLV. The range may + be represented as either a range of MPLS labels, or numeric segment + identifiers"; + } + + leaf first-value { + type uint32; + description + "The first value within the SRGB range being specified. The type of the + entry is determined based on the value of the entry type as this value + may represent either a segment identifier or an MPLS label."; + } + } + + grouping ospfv2-lsdb-extended-prefix-state { + description + "State parameters relating to an Extended Prefix LSA"; + + leaf route-type { + type enumeration { + enum UNSPECIFIED { + value 0; + description + "The prefix described in the extended prefix LSA is of an + unspecified type"; + } + enum INTRA_AREA { + value 1; + description + "The prefix described in the extended prefix LSA is an intra-area + prefix for the advertising system"; + } + enum INTER_AREA { + value 3; + description + "The prefix described in the extended prefix LSA is an inter-area + prefix for the advertising system"; + } + enum AS_EXTERNAL { + value 5; + description + "The prefix described in the extended prefix LSA is external to the + autonomous system of the advertising system"; + } + enum NSSA_EXTERNAL { + value 7; + description + "The prefix described in the extended prefix LSA externally + advertised from an NSSA area visibile to the advertising system"; + } + } + description + "The type of prefix that is contained within the Extended Prefix LSA. + The information contained in sub-TLVs of the attribute is applicable + regardless of this value."; + } + + uses ospfv2-lsdb-common-prefix-properties; + + leaf attached { + type boolean; + default false; + description + "If this value is set to true, the prefix being advertised was + generated by an ABR for an inter-area prefix. The value corresponds + to the A-flag of the flags field of the Extended Prefix LSA"; + } + + leaf node { + type boolean; + default false; + description + "If this value is set to true, the prefix being advertised represents + the advertising router. Typically, the prefix within the LSA is + expected to be globally-reachable prefix associated with a loopback + interface"; + } + + leaf prefix { + type inet:ipv4-address-no-zone; + description + "The IPv4 prefix contained within the extended prefix LSA"; + } + } + + grouping ospfv2-lsdb-extended-link-state { + description + "State parameters related to an extended link LSA"; + + leaf link-type { + type identityref { + base "oc-ospf-types:OSPFV2_ROUTER_LINK_TYPE"; + } + description + "The type of link with which extended attributes are associated"; + } + + uses ospfv2-lsdb-common-link-specification; + + } + + grouping ospfv2-lsdb-extended-link-tlv-state { + description + "State parameters relating to a sub-TLV of the extended link LSA"; + + leaf type { + type identityref { + base "oc-ospf-types:OSPFV2_EXTENDED_LINK_SUBTLV_TYPE"; + } + description + "The type of the sub-TLV contained within the extended link TLV"; + } + } + + grouping ospfv2-lsdb-extended-prefix-tlv-state { + description + "State parameters related to a sub-TLV of an Extended Prefix LSA"; + + leaf type { + type identityref { + base "oc-ospf-types:OSPFV2_EXTENDED_PREFIX_SUBTLV_TYPE"; + } + description + "The type of sub-TLV as indicated by the Extended Prefix LSA"; + } + } + + grouping ospfv2-lsdb-extended-prefix-range-state { + description + "Parameters of the Extended Prefix Range SubTLV"; + + uses ospfv2-lsdb-common-prefix-properties; + + leaf range-size { + type uint16; + description + "The number of prefixes that are covered by the advertisement."; + } + + leaf inter-area { + type boolean; + default false; + description + "When this leaf is set to true, then the prefix range is inter-area - + the flag is set by the ABR that advertises the Extended Prefix Range + TLV"; + } + + leaf prefix { + type inet:ipv4-prefix; + description + "The first prefix in the range of prefixes being described by the + extended prefix range sub-TLV"; + } + } + + grouping ospfv2-lsdb-extended-prefix-prefix-sid-state { + description + "Parameters of the Prefix-SID sub-TLV"; + + leaf no-php { + type boolean; + default false; + description + "If this leaf is set the advertising system has indicated that the + prefix SID must not be popped before delivering packets to it"; + } + + leaf mapping-server { + type boolean; + default false; + description + "If this leaf is set the SID was advertised by a Segment Routing + mapping server"; + } + + leaf explicit-null { + type boolean; + default false; + description + "If this leaf is set, the advertising system has requested that the + prefix SID value should be replaced with the explicit null label + value"; + } + + leaf sid-value-type { + type enumeration { + enum ABSOLUTE { + description + "The SID contained in the Prefix-SID sub-TLV is an absolute + value"; + } + enum INDEX { + description + "The SID contained in the Prefix-SID sub-TLV is an index to the + SRGB"; + } + } + description + "Specifies the type of the value specified within the Prefix SID + sub-TLV - in particular, whether the value is an index or an + absolute value. This value corresponds with the V-flag of the Prefix + SID sub-TLV"; + } + + leaf sid-scope { + type enumeration { + enum LOCAL { + description + "The value of the SID is + significant only to the advertising system"; + } + enum GLOBAL { + description + "The value of the SID is globally significant"; + } + } + description + "Specifies the scope of the SID advertisement within the Prefix SID + sub-TLV. The scope of the SID is independent of whether the SID + contained is an index, or an absolute value"; + } + + leaf multi-topology-identifier { + type uint8; + description + "The identifier for the topology to which the Prefix SID relates. The + value of this leaf is a MT-ID as defined in RFC4915"; + } + + leaf algorithm { + type uint8; + description + "The algorithm that computes the path associated with the Prefix SID"; + } + + leaf sid-value { + type uint32; + description + "The value of the Prefix SID. The meaning of this value is dependent + upon the type of SID, and its scope. The value contained is either a + 32-bit value indicating the index of the SID, or a 24-bit label where + the 20 right-most bits are used for encoding the label value"; + } + } + + grouping ospfv2-lsdb-extended-prefix-sid-label-binding-state { + description + "State parameters relating to the extended prefix SID SID/Label binding + sub-TLV"; + + leaf mirroring { + type boolean; + default false; + description + "When set to true, this indicates that the SID/Label Binding sub-TLV + entries contained within this TLV are indicative of a mirroring + context"; + } + + leaf multi-topology-identifier { + type uint8; + description + "The identifier for the topology to which the SID/Label Binding + sub-TLV is associated. The value of this leaf is a MT-ID as defined + in RFC4915"; + } + + leaf weight { + type uint8; + description + "The weight of the advertised binding when used for load-balancing + purposes"; + } + } + + grouping ospfv2-lsdb-extended-prefix-sid-label-binding-tlv-state { + description + "State parameters directly relating to the SID/Label Binding TLV"; + + leaf type { + type identityref { + base + "oc-ospf-types:OSPFV2_EXTENDED_PREFIX_SID_LABEL_BINDING_SUBTLV_TYPE"; + } + description + "The type of sub-TLV that is being contained within the SID/Label + sub-TLV"; + } + } + + grouping ospfv2-lsdb-extprefix-sid-label-binding-state { + description + "State parameters relating to the SID/Label binding sub-TLV of the + SID/Label/Binding TLV"; + + uses ospfv2-lsdb-common-sr-sid-spec; + } + + grouping ospfv2-lsdb-extprefix-sid-label-ero-metric-state { + description + "State parameters relating to the ERO Metric Sub-TLV of the SID/Label + Binding TLV"; + + leaf metric { + type uint32; + description + "The metric representing the aggregate IGP or TE path cost for the + binding included within the SID/Label Binding TLV"; + } + } + + grouping ospfv2-lsdb-extprefix-sid-lbl-ero-path-seg-state { + description + "State parameters relating to the a segment included within the + ERO Path Sub-TLV of the SID/Label Binding TLV"; + + leaf type { + type identityref { + base "oc-ospf-types:OSPFV2_EXTPREFIX_BINDING_ERO_PATH_SEGMENT_TYPE"; + } + description + "The type of the segment being specified as part of the ERO"; + } + + leaf loose { + type boolean; + default false; + description + "If this leaf is set the segment is identifier as a loose path + segment, otherwise the path strictly follows the path specified"; + } + } + + grouping ospfv2-lsdb-extprefix-sid-lbl-ero-ipv4-state { + description + "State parameters relating to an IPv4 address segment included within + the ERO path"; + + leaf address { + type inet:ipv4-address-no-zone; + description + "The IPv4 address of the hop within the ERO"; + } + } + + grouping ospfv2-lsdb-extprefix-sid-lbl-ero-unnum-state { + description + "State parameters relating to an unnumbered hop within the ERO path"; + + leaf router-id { + type inet:ipv4-address-no-zone; + description + "The IPv4 router identtifier of the remote system"; + } + + leaf interface-id { + type uint32; + description + "The identifier assigned to the link by the remote system"; + } + } + + grouping ospfv2-lsdb-extended-link-adj-sid-state { + description + "State parameters relating to the Adjacency SID sub-TLV of the + Extended Link LSA"; + + leaf backup { + type boolean; + default false; + description + "When this flag is set, it indicates that the adjacency SID refers to + an adjacency which is eligible for protection"; + } + + leaf group { + type boolean; + default false; + description + "When this flag is set it indicates that the adjacency SID refers to + a group of adjacencies that have a common value"; + } + + uses ospfv2-lsdb-common-sr-sid-spec; + + leaf weight { + type uint8; + description + "The weight of the Adjacency SID when used for load-balancing"; + } + + leaf multi-topology-identifier { + type uint8; + description + "The multi-topology identifier with which the adjacency SID is + associated"; + } + } + + grouping ospfv2-lsdb-structure { + description + "Structural grouping for per-area LSDB contents"; + + container lsdb { + // Top-level RO, if this were ever to become writeable then + // the state containers lower down need config false added + config false; + description + "The link-state database for the OSPFv2 area"; + + container state { + description + "Operational state parameters relating to the OSPFv2 + area"; + + uses ospfv2-lsdb-area-state; + } + + container lsa-types { + description + "Enclosing container for a list of LSA types that are + in the LSDB for the specified area"; + + list lsa-type { + key "type"; + + description + "List of LSA types in the LSDB for the specified + area"; + + leaf type { + type leafref { + path "../state/type"; + } + description + "A reference for the LSA type being described within + the LSDB"; + } + + container state { + description + "Top-level operational state parameters relating to + an LSA within the area"; + uses ospfv2-lsdb-area-lsa-type-state; + } + + container lsas { + description + "Enclosing container for a list of the LSAs of + the specified type received by the system"; + + list lsa { + key "link-state-id"; + + description + "List of the LSAs of a specified type in the + LSDB for the specified area"; + + leaf link-state-id { + type leafref { + path "../state/link-state-id"; + } + description + "Reference to the Link State ID of the LSA"; + } + + container state { + description + "Operational state parameters relating to all + LSA types"; + uses ospfv2-lsdb-area-lsa-state; + } + + uses ospfv2-lsdb-router-lsa-structure { + when "../../state/type = 'ROUTER_LSA'" { + description + "Include the router LSA hierarchy solely when + that LSA type is being described"; + } + } + + uses ospfv2-lsdb-network-lsa-structure { + when "../../state/type = 'NETWORK_LSA'" { + description + "Include the network LSA hierarchy solely when + that LSA type is being described"; + } + } + + uses ospfv2-lsdb-summary-lsa-structure { + // rjs TODO: check this syntax + when "../../state/type = " + + "'SUMMARY_IP_NETWORK_LSA' or " + + "../../state/type = 'SUMMARY_ASBR_LSA'" { + description + "Include the summary LSA hierarchy solely when + that LSA type is being described"; + } + } + + uses ospfv2-lsdb-asexternal-lsa-structure { + when "../../state/type = 'AS_EXTERNAL_LSA'" { + description + "Include the AS external LSA hierarchy solely when + that LSA type is being described"; + } + } + + uses ospfv2-lsdb-nssa-external-lsa-structure { + when "../../state/type = 'NSSA_AS_EXTERNAL_LSA'" { + description + "Include the NSSA External LSA hierarchy solely + when that LSA type is being described"; + } + } + + uses ospfv2-lsdb-opaque-lsa-structure { + when "../../state/type = 'OSPFV2_LINK_SCOPE_OPAQUE_LSA' + or ../../state/type = 'OSPFV2_AREA_SCOPE_OPAQUE_LSA' + or ../../state/type = 'OSPFV2_AS_SCOPE_OPAQUE_LSA'" { + description + "Include the Opaque LSA structure when type of entry + being described in an opaque LSA"; + } + } + } + } + } + } + } + } +} diff --git a/models/yang/common/openconfig-ospfv2.yang b/models/yang/common/openconfig-ospfv2.yang new file mode 100644 index 0000000000..5a0f39a8ee --- /dev/null +++ b/models/yang/common/openconfig-ospfv2.yang @@ -0,0 +1,109 @@ +module openconfig-ospfv2 { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/ospfv2"; + + prefix "oc-ospfv2"; + + // import some basic types + //import ietf-inet-types { prefix inet; } + import openconfig-extensions { prefix "oc-ext"; } + + // Include submodules + // Global: All global context groupings; + include openconfig-ospfv2-global; + // Area: Config/opstate for an area + include openconfig-ospfv2-area; + // Area Interface: Config/opstate for an Interface + include openconfig-ospfv2-area-interface; + // LSDB: Operational state model covering the LSDB + include openconfig-ospfv2-lsdb; + // Common: Content included in >1 context + include openconfig-ospfv2-common; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "An OpenConfig model for Open Shortest Path First (OSPF) + version 2"; + + oc-ext:openconfig-version "0.1.3"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.3"; + } + + revision "2018-06-05" { + description + "Bug fixes in when statements in lsdb"; + reference "0.1.2"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes."; + reference "0.1.1"; + } + + revision "2017-02-28"{ + description + "Initial public release of OSPFv2"; + reference "0.1.0"; + } + + revision "2016-06-24" { + description + "Initial revision"; + reference "0.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping ospfv2-top { + description + "Top-level OSPF configuration and operational state"; + + container ospfv2 { + description + "Top-level configuration and operational state for + Open Shortest Path First (OSPF) v2"; + + uses ospfv2-global-structural; + + container areas { + description + "Configuration and operational state relating to an + OSPFv2 area."; + + list area { + key "identifier"; + + description + "The OSPFv2 areas within which the local system exists"; + + leaf identifier { + type leafref { + path "../config/identifier"; + } + description + "A reference to the identifier for the area."; + } + + uses ospfv2-area-structure; + } + } + } + } +} diff --git a/models/yang/common/openconfig-packet-match-types.yang b/models/yang/common/openconfig-packet-match-types.yang new file mode 100644 index 0000000000..1b93d52059 --- /dev/null +++ b/models/yang/common/openconfig-packet-match-types.yang @@ -0,0 +1,309 @@ +module openconfig-packet-match-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/packet-match-types"; + + prefix "oc-pkt-match-types"; + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines common types for use in models requiring + data definitions related to packet matches."; + + oc-ext:openconfig-version "1.0.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "1.0.2"; + } + + revision "2018-04-15" { + description + "Corrected description and range for ethertype typedef"; + reference "1.0.1"; + } + + revision "2017-05-26" { + description + "Separated IP matches into AFs"; + reference "1.0.0"; + } + + revision "2016-08-08" { + description + "OpenConfig public release"; + reference "0.2.0"; + } + + revision "2016-04-27" { + description + "Initial revision"; + reference "TBD"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + + // extension statements + + // feature statements + + // identity statements + + + //TODO: should replace this with an official IEEE module + // when available. Only a select number of types are + // defined in this identity. + identity ETHERTYPE { + description + "Base identity for commonly used Ethertype values used + in packet header matches on Ethernet frames. The Ethertype + indicates which protocol is encapsulated in the Ethernet + payload."; + reference + "IEEE 802.3"; + } + + identity ETHERTYPE_IPV4 { + base ETHERTYPE; + description + "IPv4 protocol (0x0800)"; + } + + identity ETHERTYPE_ARP { + base ETHERTYPE; + description + "Address resolution protocol (0x0806)"; + } + + identity ETHERTYPE_VLAN { + base ETHERTYPE; + description + "VLAN-tagged frame (as defined by IEEE 802.1q) (0x8100). Note + that this value is also used to represent Shortest Path + Bridging (IEEE 801.1aq) frames."; + } + + identity ETHERTYPE_IPV6 { + base ETHERTYPE; + description + "IPv6 protocol (0x86DD)"; + } + + identity ETHERTYPE_MPLS { + base ETHERTYPE; + description + "MPLS unicast (0x8847)"; + } + + identity ETHERTYPE_LLDP { + base ETHERTYPE; + description + "Link Layer Discovery Protocol (0x88CC)"; + } + + identity ETHERTYPE_ROCE { + base ETHERTYPE; + description + "RDMA over Converged Ethernet (0x8915)"; + } + + + //TODO: should replace this with an official IANA module when + //available. Only a select set of protocols are defined with + //this identity. + identity IP_PROTOCOL { + description + "Base identity for commonly used IP protocols used in + packet header matches"; + reference + "IANA Assigned Internet Protocol Numbers"; + } + + identity IP_TCP { + base IP_PROTOCOL; + description + "Transmission Control Protocol (6)"; + } + + identity IP_UDP { + base IP_PROTOCOL; + description + "User Datagram Protocol (17)"; + } + + identity IP_ICMP { + base IP_PROTOCOL; + description + "Internet Control Message Protocol (1)"; + } + + identity IP_IGMP { + base IP_PROTOCOL; + description + "Internet Group Membership Protocol (2)"; + } + + identity IP_PIM { + base IP_PROTOCOL; + description + "Protocol Independent Multicast (103)"; + } + + identity IP_RSVP { + base IP_PROTOCOL; + description + "Resource Reservation Protocol (46)"; + } + + identity IP_GRE { + base IP_PROTOCOL; + description + "Generic Routing Encapsulation (47)"; + } + + identity IP_AUTH { + base IP_PROTOCOL; + description + "Authentication header, e.g., for IPSEC (51)"; + } + + identity IP_L2TP { + base IP_PROTOCOL; + description + "Layer Two Tunneling Protocol v.3 (115)"; + } + + + + identity TCP_FLAGS { + description + "Common TCP flags used in packet header matches"; + reference + "IETF RFC 793 - Transmission Control Protocol + IETF RFC 3168 - The Addition of Explicit Congestion + Notification (ECN) to IP"; + } + + identity TCP_SYN { + base TCP_FLAGS; + description + "TCP SYN flag"; + } + + identity TCP_FIN { + base TCP_FLAGS; + description + "TCP FIN flag"; + } + + identity TCP_RST { + base TCP_FLAGS; + description + "TCP RST flag"; + } + + identity TCP_PSH { + base TCP_FLAGS; + description + "TCP push flag"; + } + + identity TCP_ACK { + base TCP_FLAGS; + description + "TCP ACK flag"; + } + + identity TCP_URG { + base TCP_FLAGS; + description + "TCP urgent flag"; + } + + identity TCP_ECE { + base TCP_FLAGS; + description + "TCP ECN-Echo flag. If the SYN flag is set, indicates that + the TCP peer is ECN-capable, otherwise indicates that a + packet with Congestion Experienced flag in the IP header + is set"; + } + + identity TCP_CWR { + base TCP_FLAGS; + description + "TCP Congestion Window Reduced flag"; + } + + // typedef statements + + typedef port-num-range { + type union { + type string { + pattern '^(6[0-5][0-5][0-3][0-5]|[0-5]?[0-9]?[0-9]?[0-9]?' + + '[0-9]?)\.\.(6[0-5][0-5][0-3][0-5]|[0-5]?[0-9]?[0-9]?' + + '[0-9]?[0-9]?)$'; + } + type oc-inet:port-number; + type enumeration { + enum ANY { + description + "Indicates any valid port number (e.g., wildcard)"; + } + } + } + description + "Port numbers may be represented as a single value, + an inclusive range as .., or as ANY to + indicate a wildcard."; + } + + typedef ip-protocol-type { + type union { + type uint8 { + range 0..254; + } + type identityref { + base IP_PROTOCOL; + } + } + description + "The IP protocol number may be expressed as a valid protocol + number (integer) or using a protocol type defined by the + IP_PROTOCOL identity"; + } + + typedef ethertype-type { + type union { + type uint16 { + range 1536..65535; + } + type identityref { + base ETHERTYPE; + } + } + description + "The Ethertype value may be expressed as a 16-bit number in + decimal notation, or using a type defined by the + ETHERTYPE identity"; + } + +} diff --git a/models/yang/common/openconfig-packet-match.yang b/models/yang/common/openconfig-packet-match.yang new file mode 100644 index 0000000000..510bc57686 --- /dev/null +++ b/models/yang/common/openconfig-packet-match.yang @@ -0,0 +1,371 @@ +module openconfig-packet-match { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/header-fields"; + + prefix "oc-pkt-match"; + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-packet-match-types { prefix oc-pkt-match-types; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines data related to packet header fields + used in matching operations, for example in ACLs. When a + field is omitted from a match expression, the effect is a + wildcard ('any') for that field."; + + oc-ext:openconfig-version "1.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "1.1.1"; + } + + revision "2017-12-15" { + description + "Add MPLS packet field matches"; + reference "1.1.0"; + } + + revision "2017-05-26" { + description + "Separated IP matches into AFs"; + reference "1.0.0"; + } + + revision "2016-08-08" { + description + "OpenConfig public release"; + reference "0.2.0"; + } + + revision "2016-04-27" { + description + "Initial revision"; + reference "TBD"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + + // Physical Layer fields + // ethernet-header + grouping ethernet-header-config { + description + "Configuration data of fields in Ethernet header."; + + leaf source-mac { + type oc-yang:mac-address; + description + "Source IEEE 802 MAC address."; + } + + leaf source-mac-mask { + type oc-yang:mac-address; + description + "Source IEEE 802 MAC address mask."; + } + + leaf destination-mac { + type oc-yang:mac-address; + description + "Destination IEEE 802 MAC address."; + } + + leaf destination-mac-mask { + type oc-yang:mac-address; + description + "Destination IEEE 802 MAC address mask."; + } + + leaf ethertype { + type oc-pkt-match-types:ethertype-type; + description + "Ethertype field to match in Ethernet packets"; + } + } + + grouping ethernet-header-state { + description + "State information of fields in Ethernet header."; + } + + grouping ethernet-header-top { + description + "Top level container for fields in Ethernet header."; + + container l2 { + description + "Ethernet header fields"; + + container config { + description + "Configuration data"; + uses ethernet-header-config; + } + + container state { + config false; + description + "State Information."; + uses ethernet-header-config; + uses ethernet-header-state; + } + } + } + + grouping mpls-header-top { + description + "Top-level container for fields in an MPLS header."; + + container mpls { + description + "MPLS header fields"; + + container config { + description + "Configuration parameters relating to fields within + the MPLS header."; + uses mpls-header-config; + } + + container state { + config false; + description + "Operational state parameters relating to fields + within the MPLS header"; + uses mpls-header-config; + } + } + } + + grouping mpls-header-config { + description + "Configuration parameters relating to matches within + MPLS header fields."; + + leaf traffic-class { + type uint8 { + range "0..7"; + } + description + "The value of the MPLS traffic class (TC) bits, + formerly known as the EXP bits."; + } + } + + grouping ip-protocol-fields-common-config { + description + "IP protocol fields common to IPv4 and IPv6"; + + leaf dscp { + type oc-inet:dscp; + description + "Value of diffserv codepoint."; + } + + leaf protocol { + type oc-pkt-match-types:ip-protocol-type; + description + "The protocol carried in the IP packet, expressed either + as its IP protocol number, or by a defined identity."; + } + + leaf hop-limit { + type uint8 { + range 0..255; + } + description + "The IP packet's hop limit -- known as TTL (in hops) in + IPv4 packets, and hop limit in IPv6"; + } + } + + // IP Layer + // ip-protocol-fields + grouping ipv4-protocol-fields-config { + description + "Configuration data of IP protocol fields + for IPv4"; + + leaf source-address { + type oc-inet:ipv4-prefix; + description + "Source IPv4 address prefix."; + } + + leaf destination-address { + type oc-inet:ipv4-prefix; + description + "Destination IPv4 address prefix."; + } + + uses ip-protocol-fields-common-config; + + } + + grouping ipv4-protocol-fields-state { + description + "State information of IP header fields for IPv4"; + } + + grouping ipv4-protocol-fields-top { + description + "IP header fields for IPv4"; + + container ipv4 { + description + "Top level container for IPv4 match field data"; + + container config { + description + "Configuration data for IPv4 match fields"; + uses ipv4-protocol-fields-config; + } + + container state { + config false; + description + "State information for IPv4 match fields"; + uses ipv4-protocol-fields-config; + uses ipv4-protocol-fields-state; + } + } + } + + grouping ipv6-protocol-fields-config { + description + "Configuration data for IPv6 match fields"; + + leaf source-address { + type oc-inet:ipv6-prefix; + description + "Source IPv6 address prefix."; + } + + leaf source-flow-label { + type oc-inet:ipv6-flow-label; + description + "Source IPv6 Flow label."; + } + + leaf destination-address { + type oc-inet:ipv6-prefix; + description + "Destination IPv6 address prefix."; + } + + leaf destination-flow-label { + type oc-inet:ipv6-flow-label; + description + "Destination IPv6 Flow label."; + } + + uses ip-protocol-fields-common-config; + } + + grouping ipv6-protocol-fields-state { + description + "Operational state data for IPv6 match fields"; + } + + grouping ipv6-protocol-fields-top { + description + "Top-level grouping for IPv6 match fields"; + + container ipv6 { + description + "Top-level container for IPv6 match field data"; + + container config { + description + "Configuration data for IPv6 match fields"; + + uses ipv6-protocol-fields-config; + } + + container state { + + config false; + + description + "Operational state data for IPv6 match fields"; + + uses ipv6-protocol-fields-config; + uses ipv6-protocol-fields-state; + } + } + } + + // Transport fields + grouping transport-fields-config { + description + "Configuration data of transport-layer packet fields"; + + leaf source-port { + type oc-pkt-match-types:port-num-range; + description + "Source port or range"; + } + + leaf destination-port { + type oc-pkt-match-types:port-num-range; + description + "Destination port or range"; + } + + leaf-list tcp-flags { + type identityref { + base oc-pkt-match-types:TCP_FLAGS; + } + description + "List of TCP flags to match"; + } + } + + grouping transport-fields-state { + description + "State data of transport-fields"; + } + + grouping transport-fields-top { + description + "Destination transport-fields top level grouping"; + + container transport { + description + "Transport fields container"; + + container config { + description + "Configuration data"; + uses transport-fields-config; + } + + container state { + config false; + description + "State data"; + uses transport-fields-config; + uses transport-fields-state; + } + } + } + +} diff --git a/models/yang/common/openconfig-pf-forwarding-policies.yang b/models/yang/common/openconfig-pf-forwarding-policies.yang new file mode 100644 index 0000000000..602174bced --- /dev/null +++ b/models/yang/common/openconfig-pf-forwarding-policies.yang @@ -0,0 +1,391 @@ +submodule openconfig-pf-forwarding-policies { + belongs-to openconfig-policy-forwarding { + prefix "oc-pf"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-packet-match { prefix "oc-pmatch"; } + import openconfig-yang-types { prefix "oc-yang"; } + import openconfig-inet-types { prefix "oc-inet"; } + + include openconfig-pf-path-groups; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains configuration and operational state + relating to the definition of policy-forwarding policies."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2017-06-21" { + description + "Amend policy forwarding model based on ACL changes."; + reference "0.2.0"; + } + + revision "2017-02-28" { + description + "Initial public release of policy forwarding."; + reference "0.1.0"; + } + + revision "2016-11-08" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping pf-forwarding-policy-structural { + description + "Structural grouping defining forwarding policies under the + policy- forwarding module."; + + container policies { + description + "Forwarding policies defined to enact policy-based forwarding + on the local system."; + + list policy { + key "policy-id"; + + description + "A forwarding policy is defined to have a set of match + criteria, allowing particular fields of a packet's header to + be matched, and a set of forwarding actions which determines + how the local system should forward the packet."; + + leaf policy-id { + type leafref { + path "../config/policy-id"; + } + description + "Reference to the identifier for the forwarding-policy."; + } + + container config { + description + "Configuration options relating to the forwarding + policy."; + uses pf-forwarding-policy-config; + } + + container state { + config false; + description + "Operational state parameters relating to the forwarding + policy."; + uses pf-forwarding-policy-config; + } + + container rules { + description + "The criteria that should be matched for a packet to be + forwarded according to the policy action."; + + list rule { + key "sequence-id"; + + description + "A match rule for the policy. In the case that multiple + criteria are specified within a single rule, all criteria + must be met for the rule to be applied to a packet."; + + leaf sequence-id { + type leafref { + path "../config/sequence-id"; + } + description + "A unique sequence identifier for the match rule."; + } + + container config { + description + "Configuration parameters relating to the match + rule."; + uses pf-forwarding-policy-rule-config; + } + + container state { + config false; + description + "Operational state parameters relating to the match + rule."; + uses pf-forwarding-policy-rule-config; + uses pf-forwarding-policy-rule-state; + } + + uses oc-pmatch:ethernet-header-top; + uses oc-pmatch:ipv4-protocol-fields-top; + uses oc-pmatch:ipv6-protocol-fields-top; + uses oc-pmatch:transport-fields-top; + + container action { + description + "The forwarding policy action to be applied for + packets matching the rule."; + + container config { + description + "Configuration parameters relating to the forwarding + rule's action."; + uses pf-forwarding-policy-action-config; + } + + container state { + config false; + description + "Operational state parameters relating to the + forwarding rule's action."; + uses pf-forwarding-policy-action-config; + } + + uses pf-forwarding-policy-action-encapsulate-gre; + } + } + } + } + } + } + + grouping pf-forwarding-policy-config { + description + "Configuration parameters relating to the forwarding policy."; + + leaf policy-id { + type string; + description + "A unique name identifying the forwarding policy. This name is + used when applying the policy to a particular interface."; + } + } + + grouping pf-forwarding-policy-rule-config { + description + "Configuration parameters relating to a policy rule."; + + leaf sequence-id { + type uint32; + description + "Unique sequence number for the policy rule."; + } + } + + grouping pf-forwarding-policy-rule-state { + description + "Operational state parameters relating to a policy rule."; + + leaf matched-pkts { + type oc-yang:counter64; + description + "Number of packets matched by the rule."; + } + + leaf matched-octets { + type oc-yang:counter64; + description + "Bytes matched by the rule."; + } + } + + grouping pf-forwarding-policy-action-config { + description + "Forwarding policy action configuration parameters."; + + leaf discard { + type boolean; + default false; + description + "When this leaf is set to true, the local system should drop + packets that match the rule."; + } + + leaf decapsulate-gre { + type boolean; + default false; + description + "When this leaf is set to true, the local system should remove + the GRE header from the packet matching the rule. Following + the decapsulation it should subsequently forward the + encapsulated packet according to the relevant lookup (e.g., if + the encapsulated packet is IP, the packet should be routed + according to the IP destination)."; + } + + leaf network-instance { + type leafref { + + // We are at: + // $NIROOT/policy-forwarding/policies/ + // policy/rules/rule/action/config/ + // network-instance + path "../../../../../../../../config/name"; + } + description + "When this leaf is set, packets matching the match criteria + for the forwarding rule should be looked up in the + network-instance that is referenced rather than the + network-instance with which the interface is associated. + Such configuration allows policy-routing into multiple + sub-topologies from a single ingress access interface, or + different send and receive contexts for a particular + interface (sometimes referred to as half-duplex VRF)."; + } + + leaf path-selection-group { + type leafref { + // We are at: + // $NIROOT/policy-forwarding/policies/ + // policy/rules/rule/action/config/to-path-group + path "../../../../../../../path-selection-groups/" + + "path-selection-group/config/group-id"; + } + description + "When path-selection-group is set, packets matching the + match criteria for the forwarding rule should be forwarded + only via one of the paths that is specified within the + referenced path-selection-group. The next-hop of the packet + within the routing context should be used to determine between + multiple paths that are specified within the group."; + } + + leaf next-hop { + type oc-inet:ip-address; + description + "When an IP next-hop is specified in the next-hop field, + packets matching the match criteria for the forwarding rule + should be forwarded to the next-hop IP address, bypassing any + lookup on the local system."; + } + } + + grouping pf-forwarding-policy-action-encapsulate-gre { + description + "Structural grouping covering the encapsulate-gre action of the + policy forwarding rule."; + + container encapsulate-gre { + description + "Packets matching the policy rule should be GRE encapsulated + towards the set of targets defined within the target list. Where + more than one target is specified, or the target subnet expands + to more than one endpoint, packets should be load-balanced across + the destination addresses within the subnets."; + + container config { + description + "Configuration parameters for the GRE encapsulation rules action."; + uses pf-forwarding-policy-action-gre-config; + } + + container state { + description + "Operational state parameters for the GRE encapsulation rule + action."; + config false; + uses pf-forwarding-policy-action-gre-config; + } + + container targets { + description + "Surrounding container for the list of GRE tunnel targets that + should be encapsulated towards."; + + list target { + key "id"; + + leaf id { + type leafref { + path "../config/id"; + } + description + "Reference to the unique identifier for the target."; + } + + description + "Each target specified within this list should be treated as a + endpoint to which packets should be GRE encapsulated. Where the + set of destinations described within a single entry expands to + more than one destination IP address, packets should be load + shared across the destination using the local system's ECMP hashing + mechanisms."; + + container config { + description + "Configuration parameters for the GRE target."; + uses pf-forwarding-policy-action-gre-target-config; + } + + container state { + description + "Operational state parameters for the GRE target."; + config false; + uses pf-forwarding-policy-action-gre-target-config; + } + } + } + } + } + + grouping pf-forwarding-policy-action-gre-config { + description + "Configuration parameters for the encapsulate-gre forwarding + policy action."; + + leaf identifying-prefix { + type oc-inet:ip-prefix; + description + "An IP prefix that can be used to identify the group of + GRE endpoints that are being encapsulated towards. Systems + that require an IP identifier for the tunnel set + should use this prefix as the next-hop identifier."; + } + } + + grouping pf-forwarding-policy-action-gre-target-config { + description + "Configuration parameters for each target of a GRE Encapsulation + rule"; + + leaf id { + type string; + description + "A unique identifier for the target."; + } + + leaf source { + type oc-inet:ip-address; + description + "The source IP address that should be used when encapsulating + packets from the local system."; + } + + leaf destination { + type oc-inet:ip-prefix; + description + "The set of destination addresses that should be encapsulated towards. + Where a subnet is specified, each address within the subnet should be + treated as an independent destination for encapsulated traffic. Packets + should be distributed with ECMP across the set of tunnel destination + addresses."; + } + + leaf ip-ttl { + type uint8; + description + "The TTL that should be specified in the IP header of the GRE packet + encapsulating the packet matching the rule."; + } + } +} diff --git a/models/yang/common/openconfig-pf-interfaces.yang b/models/yang/common/openconfig-pf-interfaces.yang new file mode 100644 index 0000000000..203755353e --- /dev/null +++ b/models/yang/common/openconfig-pf-interfaces.yang @@ -0,0 +1,127 @@ +submodule openconfig-pf-interfaces { + belongs-to openconfig-policy-forwarding { + prefix "oc-pf"; + } + + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-interfaces { prefix "oc-if"; } + + include openconfig-pf-forwarding-policies; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains groupings related to the association + between interfaces and policy forwarding rules."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2017-06-21" { + description + "Amend policy forwarding model based on ACL changes."; + reference "0.2.0"; + } + + revision "2017-02-28" { + description + "Initial public release of policy forwarding."; + reference "0.1.0"; + } + + revision "2016-11-08" { + description + "Initial revision"; + reference "0.0.1"; + } + + + grouping pf-interfaces-structural { + description + "Structural grouping for interface to forwarding policy bindings + within the policy-forwarding model."; + + container interfaces { + description + "Configuration and operational state relating policy + forwarding on interfaces."; + + list interface { + key "interface-id"; + + description + "Configuration and operationals state relating to the + relationship between interfaces and policy-based forwarding + rules."; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "A reference to the unique identifier for the interface + being referenced by the policy."; + } + + container config { + description + "Configuration parameters relating to an interface to + policy forwarding rule binding."; + + uses pf-interface-config; + } + + container state { + config false; + description + "Operational state parameters relating to an interface to + policy forwarding rule binding."; + + uses pf-interface-config; + } + + uses oc-if:interface-ref; + } + } + } + + grouping pf-interface-config { + description + "Configuration parameters relating to an interface to policy + forwarding rule binding."; + + leaf interface-id { + type oc-if:interface-id; + description + "A unique identifier for the interface."; + } + + leaf apply-forwarding-policy { + type leafref { + // We are at /network-instances/network-instance/ + // policy-forwarding/interfaces/interface/config/ + // apply-forwarding-policy + path "../../../../policies/policy/" + + "config/policy-id"; + } + description + "The policy to be applied on the interface. Packets ingress on + the referenced interface should be compared to the match + criteria within the specified policy, and in the case that + these criteria are met, the forwarding actions specified + applied. These policies should be applied following quality of + service classification, and ACL actions if such entities are + referenced by the corresponding interface."; + } + } +} diff --git a/models/yang/common/openconfig-pf-path-groups.yang b/models/yang/common/openconfig-pf-path-groups.yang new file mode 100644 index 0000000000..c9dd8a1072 --- /dev/null +++ b/models/yang/common/openconfig-pf-path-groups.yang @@ -0,0 +1,131 @@ +submodule openconfig-pf-path-groups { + belongs-to openconfig-policy-forwarding { + prefix "oc-pf"; + } + + import openconfig-extensions { prefix "oc-ext"; } + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains configuration and operational state + relating to path-selection-groups which are used to group + forwarding entities together to be used as policy forwarding + targets."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2017-06-21" { + description + "Amend policy forwarding model based on ACL changes."; + reference "0.2.0"; + } + + revision "2017-02-28" { + description + "Initial public release of policy forwarding."; + reference "0.1.0"; + } + + revision "2016-11-08" { + description + "Initial revision"; + reference "0.0.1"; + } + + grouping pf-path-groups-structural { + description + "Structural grouping containing the definition of path groups + within the context of policy-based forwarding."; + + container path-selection-groups { + description + "Surrounding container for the path selection groups defined + within the policy forwarding model."; + + list path-selection-group { + key "group-id"; + + leaf group-id { + type leafref { + path "../config/group-id"; + } + description + "Reference to a unique identifier for the path selection + group"; + + } + + description + "A path selection group is a set of forwarding resources, + which are grouped as eligible paths for a particular + policy-based forwarding rule. A policy rule may select a + path-selection-group as the egress for a particular type of + traffic (e.g., DSCP value). The system then utilises its + standard forwarding lookup mechanism to select from the + paths that are specified within the group - for IP packets, + the destination IP address is used such that the packet is + routed to the entity within the path-selection-group that + corresponds to the next-hop for the destination IP address + of the packet; for L2 packets, the selection is based on the + destination MAC address. If multiple paths within the + selection group are eligible to be used for forwarding, + the packets are load-balanced between them according to + the system's usual load balancing logic."; + + container config { + description + "Configuration parameters relating to the path selection + group."; + uses pf-path-selection-group-config; + } + + container state { + config false; + description + "Operational state parameters relating to the path + selection group."; + uses pf-path-selection-group-config; + } + } + } + } + + grouping pf-path-selection-group-config { + description + "Configuration parameters relating to a path selection group."; + + leaf group-id { + type string; + description + "A unique name for the path-selection-group"; + } + + leaf-list mpls-lsp { + type leafref { + // We are at /network-instances/network-instance/ + // policy-forwarding/path-selection-groups/ + // path-selection-group/config/mpls-lsp + path "../../../../../mpls/lsps/constrained-path/tunnels/" + + "tunnel/config/name"; + } + description + "A set of MPLS constrained-path LSPs which should be + considered for the policy forwarding next-hop. In order to + select between the LSPs within the path-selection-group, the + system should determine which LSP provides the best path to + the next-hop for the routed packet."; + } + } +} diff --git a/models/yang/common/openconfig-pim-types.yang b/models/yang/common/openconfig-pim-types.yang new file mode 100644 index 0000000000..a1fc515cf7 --- /dev/null +++ b/models/yang/common/openconfig-pim-types.yang @@ -0,0 +1,85 @@ +module openconfig-pim-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/pim/types"; + + prefix "oc-pim-types"; + + // import some basic types + import openconfig-extensions { prefix "oc-ext"; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines types related to the PIM protocol model."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2018-02-19" { + description + "Initial revision."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity PIM_MODE { + description + "Base identity for the operating modes of Protocol-Independent + Multicast."; + } + + identity PIM_MODE_SPARSE { + base PIM_MODE; + description + "PIM sparse mode (PIM-SM)."; + reference "RFC7761"; + } + + identity PIM_MODE_DENSE { + base PIM_MODE; + description + "PIM dense mode (PIM-DM)."; + reference "RFC3973"; + } + + // typedef statements + + typedef dr-priority-type { + type uint32; + description + "The port's designated router priority. Larger always preferred. + DR Priority is a 32-bit unsigned number, ranges 0-4294967295."; + reference "RFC7761 4.3.1 page 33"; + } + + typedef pim-interval-type { + type uint8 { + range 1..255; + } + units "seconds"; + description + "Interval at which the router sends the PIM message toward the + upstream RPF neighbor."; + reference "RFC7761 4.5 page 44, 4.3.1 page 29"; + } +} diff --git a/models/yang/common/openconfig-pim.yang b/models/yang/common/openconfig-pim.yang new file mode 100644 index 0000000000..ddb4b48f1a --- /dev/null +++ b/models/yang/common/openconfig-pim.yang @@ -0,0 +1,475 @@ +module openconfig-pim { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/pim"; + + prefix "oc-pim"; + + // import some basic types/interfaces + import openconfig-pim-types { prefix oc-pim-types; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-acl { prefix oc-acl; } + import openconfig-types { prefix "oc-types"; } + import openconfig-extensions { prefix "oc-ext"; } + import ietf-inet-types { prefix "inet"; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "An OpenConfig model for Protocol Independent Multicast (PIM)."; + + oc-ext:openconfig-version "0.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.1"; + } + + revision "2018-02-09" { + description + "Initial revision."; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping admin-config { + description + "Re-usable grouping to enable or disable a particular feature."; + + leaf enabled { + type boolean; + default false; + description + "When set to true, the functionality within which this + leaf is defined is enabled, when set to false it is + explicitly disabled."; + } + } + + grouping pim-counters-state { + description + "Counters related to PIM messages."; + + leaf hello-messages { + type uint32; + description + "Number of hello messages received."; + reference "RFC7761 4.9.2 page 108"; + } + + leaf join-prune-messages { + type uint32; + description + "Number of join/prune messages received."; + reference "RFC7761 4.5 page 44"; + } + + leaf bootstrap-messages { + type uint32; + description + "Number of bootstrap router messages received."; + reference "RFC7761 3.7 page 12"; + } + } + + grouping pim-interface-config { + description + "Configuration data for PIM on each interface."; + + uses admin-config; + + leaf interface-id { + type oc-if:interface-id; + description + "Reference to an interface on which PIM is enabled."; + } + + leaf mode { + type identityref { + base oc-pim-types:PIM_MODE; + } + description + "PIM mode to use when delivering multicast traffic via this + interface."; + } + + leaf bsr-border { + type boolean; + default false; + description + "When set to true the device will not send bootstrap router + messages over this interface. By default these are transmitted + over all PIM sparse mode (PIM-SM) enabled interfaces."; + } + + leaf border-router { + type boolean; + default false; + description + "When set to true the interface is set as MBR (multicast border + router) and allows multicast traffic from sources that are + outside of the PIM domain."; + } + + + leaf dr-priority { + type oc-pim-types:dr-priority-type; + description + "The designated router priority of this interface. Larger always + preferred."; + } + + leaf join-prune-interval { + type oc-pim-types:pim-interval-type; + description + "Interval at which the router sends the PIM join/prune messages + toward the upstream RPF neighbor."; + } + + leaf hello-interval { + type oc-pim-types:pim-interval-type; + description + "Interval at which the router sends the PIM hello messages."; + } + + leaf dead-timer { + type uint16 { + range 1..65535; + } + description + "Number of missed hello messages after which a neighbor is + expired."; + } + } + + grouping pim-neighbor-state { + description + "PIM neighbor state."; + + leaf neighbor-address { + type inet:ipv4-address; + description + "IPv4 address of neighbor router."; + } + + leaf dr-address { + type inet:ipv4-address; + description + "IPv4 address of designated router."; + } + + leaf neighbor-established { + type oc-types:timeticks64; + description + "This timestamp indicates the time that the + PIM neighbor adjacency established. The value is the + timestamp in seconds relative to the Unix Epoch + (Jan 1, 1970 00:00:00 UTC). + + The PIM session uptime can be computed by clients + as the difference between this value and the + current time in UTC."; + } + + leaf neighbor-expires { + type oc-types:timeticks64; + description + "This timestamp indicates the time that the + PIM neighbor adjacency will expire should hello + messages fail to arrive from the neighbor. + The value is the timestamp in seconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + leaf mode { + type identityref { + base oc-pim-types:PIM_MODE; + } + description + "PIM mode in use when delivering multicast traffic + via this neighbor."; + } + } + + grouping pim-neighbors-top { + description + "Details about PIM neighbors."; + + container neighbors { + config false; + description + "Details about PIM neighbors."; + + list neighbor { + key "neighbor-address"; + description + "Details about a specific PIM neighbor."; + + leaf neighbor-address { + type leafref { + path "../state/neighbor-address"; + } + description + "IPv4 address of neighbor router."; + } + + container state { + config false; + description + "Details about a specific PIM neighbor."; + + uses pim-neighbor-state; + } + } + } + } + + grouping pim-interfaces-top { + description + "Configuration and state data for PIM on each interface."; + + container interfaces { + description + "Configuration and state data for PIM on each interface."; + + list interface { + key "interface-id"; + description + "This container defines interface PIM configuration and + state information."; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "Reference to an interface on which PIM is enabled."; + } + + container config { + description + "PIM interface configuration."; + + uses pim-interface-config; + } + + container state { + config false; + description + "State information for PIM interfaces."; + + uses pim-interface-config; + container counters { + description + "PIM counters for each interface."; + + uses pim-counters-state; + } + } + + uses pim-neighbors-top; + uses oc-if:interface-ref; + } + } + } + + grouping pim-global-state { + description + "State and session data for PIM on each interface."; + + leaf neighbor-count { + type uint8; + description + "Number of adjacent PIM neighbors."; + } + + container counters { + description + "Global PIM counters."; + + uses pim-counters-state; + } + } + + grouping pim-sources-joined-top { + description + "List of multicast sources joined."; + + container sources-joined { + config false; + description + "List of multicast sources joined."; + + list source { + key "address"; + description + "A multicast source that has been joined."; + + leaf address { + type leafref { + path "../state/address"; + } + description + "Source address of multicast."; + } + + container state { + config false; + description + "State for a multicast source that has been joined."; + + leaf address { + type inet:ipv4-address; + description + "Source address of multicast."; + } + + leaf group { + type inet:ipv4-address; + description + "Multicast address."; + } + + leaf upstream-interface-id { + type oc-if:interface-id; + description + "The upstream interface for this multicast source."; + } + } + } + } + } + + grouping pim-global-ssm-config { + description + "Source specific multicast (SSM) configuration."; + + leaf ssm-ranges { + type leafref { + path "/oc-acl:acl/oc-acl:acl-sets/oc-acl:acl-set/" + + "oc-acl:config/oc-acl:name"; + } + description + "List of accepted source specific multicast (SSM) address + ranges."; + } + } + + grouping pim-global-rp-addresses-config { + description + "Defines rendezvous points for sparse mode multicast."; + + leaf address { + type inet:ipv4-address; + description + "IPv4 address of rendezvous point."; + } + + leaf multicast-groups { + type string; + // TODO should this be an ACL or prefix-list reference or prefix list? + // Cisco it's an ACL, Juniper it's an inline prefix list + description + "List of multicast groups (multicast IP address ranges) for which + this entry will be used as a rendezvous point. When not + present the default is equivalent to all valid IP multicast + addresses."; + } + } + + grouping pim-global-top { + description + "Top level grouping for global PIM configuration."; + + container ssm { + description + "Source specific multicast (SSM)."; + + container config { + description + "Configuration for source specific multicast (SSM)."; + uses pim-global-ssm-config; + } + container state { + config false; + description + "State for source specific multicast (SSM)."; + uses pim-global-ssm-config; + } + } + + container rendezvous-points { + description + "Defines rendezvous points for sparse mode multicast."; + + list rendezvous-point { + key "address"; + description + "Defines a rendezvous point (RP) for sparse mode multicast."; + + leaf address { + type leafref { + path "../config/address"; + } + description + "IPv4 address of rendezvous point."; + } + + container config { + description + "Rendezvous point configuration."; + uses pim-global-rp-addresses-config; + } + container state { + config false; + description + "Rendezvous point state."; + uses pim-global-rp-addresses-config; + } + } + } + + container state { + config false; + description + "Global PIM state."; + uses pim-global-state; + } + + uses pim-sources-joined-top; + } + + grouping pim-top { + description + "Top-level grouping for PIM."; + + container pim { + description + "Top-level PIM configuration and operational state."; + + container global { + description + "This container defines global PIM configuration and state + information."; + uses pim-global-top; + } + + uses pim-interfaces-top; + } + } + + // data definition statements +} diff --git a/models/yang/common/openconfig-platform-types.yang b/models/yang/common/openconfig-platform-types.yang new file mode 100644 index 0000000000..8dc3ffc1fd --- /dev/null +++ b/models/yang/common/openconfig-platform-types.yang @@ -0,0 +1,347 @@ +module openconfig-platform-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/platform-types"; + + prefix "oc-platform-types"; + + import openconfig-types { prefix oc-types; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines data types (e.g., YANG identities) + to support the OpenConfig component inventory model."; + + oc-ext:openconfig-version "1.0.0"; + + revision "2019-06-03" { + description + "Add OpenConfig component operating system patch type."; + reference "1.0.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.10.1"; + } + + revision "2018-11-16" { + description + "Added FEC_MODE_TYPE and FEC_STATUS_TYPE"; + reference "0.10.0"; + } + + revision "2018-05-05" { + description + "Added min-max-time to + avg-min-max-instant-stats-precision1-celsius, + added new CONTROLLER_CARD identity"; + reference "0.9.0"; + } + + revision "2018-01-16" { + description + "Added new per-component common data; add temp alarm"; + reference "0.8.0"; + } + + revision "2017-12-14" { + description + "Added anchor containers for component data, added new + component types"; + reference "0.7.0"; + } + + revision "2017-08-16" { + description + "Added power state enumerated type"; + reference "0.6.0"; + } + + revision "2016-12-22" { + description + "Added temperature state variable to component"; + reference "0.5.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + + grouping avg-min-max-instant-stats-precision1-celsius { + description + "Common grouping for recording temperature values in + Celsius with 1 decimal precision. Values include the + instantaneous, average, minimum, and maximum statistics"; + + leaf instant { + type decimal64 { + fraction-digits 1; + } + units celsius; + description + "The instantaneous value of the statistic."; + } + + leaf avg { + type decimal64 { + fraction-digits 1; + } + units celsius; + description + "The arithmetic mean value of the statistic over the + sampling period."; + } + + leaf min { + type decimal64 { + fraction-digits 1; + } + units celsius; + description + "The minimum value of the statistic over the sampling + period"; + } + + leaf max { + type decimal64 { + fraction-digits 1; + } + units celsius; + description + "The maximum value of the statistic over the sampling + period"; + } + + uses oc-types:stat-interval-state; + uses oc-types:min-max-time; + } + + // identity statements + + identity OPENCONFIG_HARDWARE_COMPONENT { + description + "Base identity for hardware related components in a managed + device. Derived identities are partially based on contents + of the IANA Entity MIB."; + reference + "IANA Entity MIB and RFC 6933"; + } + + + identity OPENCONFIG_SOFTWARE_COMPONENT { + description + "Base identity for software-related components in a managed + device"; + } + + // hardware types + + identity CHASSIS { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Chassis component, typically with multiple slots / shelves"; + } + + identity BACKPLANE { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Backplane component for aggregating traffic, typically + contained in a chassis component"; + } + + identity FABRIC { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Interconnect between ingress and egress ports on the + device (e.g., a crossbar switch)."; + } + + identity POWER_SUPPLY { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Component that is supplying power to the device"; + } + + identity FAN { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Cooling fan, or could be some other heat-reduction component"; + } + + identity SENSOR { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Physical sensor, e.g., a temperature sensor in a chassis"; + } + + identity FRU { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Replaceable hardware component that does not have a more + specific defined schema."; + } + + identity LINECARD { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Linecard component, typically inserted into a chassis slot"; + } + + identity CONTROLLER_CARD { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "A type of linecard whose primary role is management or control + rather than data forwarding."; + } + + identity PORT { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Physical port, e.g., for attaching pluggables and networking + cables"; + } + + identity TRANSCEIVER { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Pluggable module present in a port"; + } + + identity CPU { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "Processing unit, e.g., a management processor"; + } + + identity STORAGE { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "A storage subsystem on the device (disk, SSD, etc.)"; + } + + identity INTEGRATED_CIRCUIT { + base OPENCONFIG_HARDWARE_COMPONENT; + description + "A special purpose processing unit, typically for traffic + switching/forwarding (e.g., switching ASIC, NPU, forwarding + chip, etc.)"; + } + + identity OPERATING_SYSTEM { + base OPENCONFIG_SOFTWARE_COMPONENT; + description + "Operating system running on a component"; + } + + identity OPERATING_SYSTEM_UPDATE { + base OPENCONFIG_SOFTWARE_COMPONENT; + description + "An operating system update - which should be a subcomponent + of the `OPERATING_SYSTEM` running on a component. An update is + defined to be a set of software changes that are atomically + installed (and uninstalled) together. Multiple updates may be + present for the Operating System. A system should not list all + installed software packages using this type -- but rather + updates that are bundled together as a single installable + item"; + } + + identity COMPONENT_OPER_STATUS { + description + "Current operational status of a platform component"; + } + + identity ACTIVE { + base COMPONENT_OPER_STATUS; + description + "Component is enabled and active (i.e., up)"; + } + + identity INACTIVE { + base COMPONENT_OPER_STATUS; + description + "Component is enabled but inactive (i.e., down)"; + } + + identity DISABLED { + base COMPONENT_OPER_STATUS; + description + "Component is administratively disabled."; + } + + identity FEC_MODE_TYPE { + description + "Base identity for FEC operational modes."; + } + + identity FEC_ENABLED { + base FEC_MODE_TYPE; + description + "FEC is administratively enabled."; + } + + identity FEC_DISABLED { + base FEC_MODE_TYPE; + description + "FEC is administratively disabled."; + } + + identity FEC_AUTO { + base FEC_MODE_TYPE; + description + "System will determine whether to enable or disable + FEC on a transceiver."; + } + + identity FEC_STATUS_TYPE { + description + "Base identity for FEC operational statuses."; + } + + identity FEC_STATUS_LOCKED { + base FEC_STATUS_TYPE; + description + "FEC is operationally locked."; + } + + identity FEC_STATUS_UNLOCKED { + base FEC_STATUS_TYPE; + description + "FEC is operationally unlocked."; + } + + // typedef statements + + typedef component-power-type { + type enumeration { + enum POWER_ENABLED { + description + "Enable power on the component"; + } + enum POWER_DISABLED { + description + "Disable power on the component"; + } + } + description + "A generic type reflecting whether a hardware component + is powered on or off"; + } + +} diff --git a/models/yang/common/openconfig-policy-forwarding.yang b/models/yang/common/openconfig-policy-forwarding.yang new file mode 100644 index 0000000000..e7597243b6 --- /dev/null +++ b/models/yang/common/openconfig-policy-forwarding.yang @@ -0,0 +1,129 @@ +module openconfig-policy-forwarding { + yang-version "1"; + + namespace "http://openconfig.net/yang/policy-forwarding"; + + prefix "oc-pf"; + + import openconfig-extensions { prefix "oc-ext"; } + + // Include submodules. + include openconfig-pf-forwarding-policies; + include openconfig-pf-path-groups; + include openconfig-pf-interfaces; + + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + relating to policy-based forwarding. Policy-based forwarding is + utilised when a system chooses how to forward packets (including + applying data-plane operations such as encapsulation or + decapsulation) based on policies other than destination L2 or L3 + header. Typically, systems may implement: + + - IP policy-based routing, where routing may be done based on the + source plus destination of an IP packet; information within the + L4 header; or some combination of both. + - Encapsulation or decapsulation based on certain policy + information - for example, matching particular IP destinations + and decapsulating GRE headers. + - Class-based selection of egress routes - such as class-based + selection of an egress MPLS path. + + The policies that are defined in this model are applied to a + particular ingress context of a network element (e.g., interface) + and are defined to apply following other interface policy such as + QoS classification and access control lists. + + This module defines: + + - policy-forwarding + | + |--- policies + | |-- policy + | |-- [match criteria] How packets are defined to + | | match policy. + | |-- [forwarding-action] How packets matching should + | be forwarded. + |--- interfaces + | |-- interfaces + | | -- apply-forwarding-policy Forwarding policy to + | used on the interface. + |--- path-selection-groups + |-- path-selection-group A group of forwarding resources + that are grouped for purposes + of next-hop selection. + + A forwarding-policy specifies the match criteria that it intends + to use to determine the packets that it reroutes - this may + consist of a number of criteria, such as DSCP. The action of the + policy results in a forwarding action being applied to matching + packets. For example, decapsulating the packet from a GRE header. + In order to enact the policy based on particular interfaces - the + forwarding-policy is applied to an interface via referencing it + within an 'apply-forwarding-policy' statement associated with an + interface. + + In some cases (e.g., Class-Based Tunnel Selection) the forwarding + action does not resolve to a single egress action, and rather + normal forwarding rules are to be applied but considering a subset + of forwarding resources. In these cases, a path-selection-group + can be created, referencing the subset of forwarding paths that + should be used for the egress selection. In the case that a subset + of MPLS LSPs are eligible for, say, DSCP 46 marked packets, a + path-selection-group is created, referencing the subset of LSPs. + The forwarding action of the corresponding policy is set to + PATH_GROUP and references the configured group of LSPs."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2017-06-21" { + description + "Amend policy forwarding model based on ACL changes."; + reference "0.2.0"; + } + + revision "2017-02-28" { + description + "Initial public release of policy forwarding."; + reference "0.1.0"; + } + + revision "2016-11-08" { + description + "Initial revision."; + reference "0.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + grouping policy-forwarding-top { + description + "Top-level grouping for Policy Forwarding"; + + container policy-forwarding { + description + "Configuration and operational state relating to policy-forwarding within + a network instance."; + + uses pf-forwarding-policy-structural; + uses pf-interfaces-structural; + uses pf-path-groups-structural; + } + } +} diff --git a/models/yang/common/openconfig-policy-types.yang b/models/yang/common/openconfig-policy-types.yang new file mode 100644 index 0000000000..46efef7f49 --- /dev/null +++ b/models/yang/common/openconfig-policy-types.yang @@ -0,0 +1,231 @@ +module openconfig-policy-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/policy-types"; + + prefix "oc-pol-types"; + + // import some basic types + import ietf-yang-types { prefix yang; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module contains general data definitions for use in routing + policy. It can be imported by modules that contain protocol- + specific policy conditions and actions."; + + oc-ext:openconfig-version "3.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.1.1"; + } + + revision "2018-06-05" { + description + "Add PIM, IGMP to INSTALL_PROTOCOL_TYPES identity"; + reference "3.1.0"; + } + + revision "2017-07-14" { + description + "Replace policy choice node/type with policy-result + enumeration;simplified defined set naming;removed generic + IGP actions; migrate to OpenConfig types; added mode for + prefix sets"; + reference "3.0.0"; + } + + revision "2016-05-12" { + description + "OpenConfig public release"; + reference "2.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity ATTRIBUTE_COMPARISON { + description + "base type for supported comparison operators on route + attributes"; + } + + identity ATTRIBUTE_EQ { + base ATTRIBUTE_COMPARISON; + description "== comparison"; + } + + identity ATTRIBUTE_GE { + base ATTRIBUTE_COMPARISON; + description ">= comparison"; + } + + identity ATTRIBUTE_LE { + base ATTRIBUTE_COMPARISON; + description "<= comparison"; + } + + typedef match-set-options-type { + type enumeration { + enum ANY { + description "match is true if given value matches any member + of the defined set"; + } + enum ALL { + description "match is true if given value matches all + members of the defined set"; + } + enum INVERT { + description "match is true if given value does not match any + member of the defined set"; + } + } + default ANY; + description + "Options that govern the behavior of a match statement. The + default behavior is ANY, i.e., the given value matches any + of the members of the defined set"; + } + + typedef match-set-options-restricted-type { + type enumeration { + enum ANY { + description "match is true if given value matches any member + of the defined set"; + } + enum INVERT { + description "match is true if given value does not match any + member of the defined set"; + } + } + default ANY; + description + "Options that govern the behavior of a match statement. The + default behavior is ANY, i.e., the given value matches any + of the members of the defined set. Note this type is a + restricted version of the match-set-options-type."; + //TODO: restriction on enumerated types is only allowed in + //YANG 1.1. Until then, we will require this additional type + } + + grouping attribute-compare-operators { + description "common definitions for comparison operations in + condition statements"; + + leaf operator { + type identityref { + base ATTRIBUTE_COMPARISON; + } + description + "type of comparison to be performed"; + } + + leaf value { + type uint32; + description + "value to compare with the community count"; + } + } + + typedef tag-type { + type union { + type uint32; + type yang:hex-string; + } + description "type for expressing route tags on a local system, + including IS-IS and OSPF; may be expressed as either decimal or + hexidecimal integer"; + reference + "RFC 2178 OSPF Version 2 + RFC 5130 A Policy Control Mechanism in IS-IS Using + Administrative Tags"; + } + + identity INSTALL_PROTOCOL_TYPE { + description + "Base type for routing protocols, including those which may + install prefixes into the RIB"; + } + + identity BGP { + base INSTALL_PROTOCOL_TYPE; + description + "BGP"; + reference + "RFC 4271"; + } + + identity ISIS { + base INSTALL_PROTOCOL_TYPE; + description + "IS-IS"; + reference + "ISO/IEC 10589"; + } + + identity OSPF { + base INSTALL_PROTOCOL_TYPE; + description + "OSPFv2"; + reference + "RFC 2328"; + } + + identity OSPF3 { + base INSTALL_PROTOCOL_TYPE; + description + "OSPFv3"; + reference + "RFC 5340"; + } + + identity STATIC { + base INSTALL_PROTOCOL_TYPE; + description + "Locally-installed static route"; + } + + identity DIRECTLY_CONNECTED { + base INSTALL_PROTOCOL_TYPE; + description + "A directly connected route"; + } + + identity LOCAL_AGGREGATE { + base INSTALL_PROTOCOL_TYPE; + description + "Locally defined aggregate route"; + } + + identity PIM { + base INSTALL_PROTOCOL_TYPE; + description + "Protocol Independent Multicast"; + reference + "RFC 7761"; + } + + identity IGMP { + base INSTALL_PROTOCOL_TYPE; + description + "Internet Group Management Protocol"; + reference + "RFC 3376"; + } +} diff --git a/models/yang/common/openconfig-procmon.yang b/models/yang/common/openconfig-procmon.yang new file mode 100644 index 0000000000..3c1013f47b --- /dev/null +++ b/models/yang/common/openconfig-procmon.yang @@ -0,0 +1,175 @@ +module openconfig-procmon { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/system/procmon"; + + prefix "oc-proc"; + + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + import openconfig-types { prefix oc-types; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module provides data definitions for process health + monitoring of one or more processes running on the system."; + + oc-ext:openconfig-version "0.3.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + grouping procmon-processes-top { + description + "Top level grouping for attributes for processes."; + + container processes { + description + "Parameters related to all monitored processes"; + + list process { + key "pid"; + config false; + description + "List of monitored processes"; + + leaf pid { + type leafref { + path "../state/pid"; + } + description + "Reference to the process pid key"; + } + + container state { + config false; + description + "State parameters related to monitored processes"; + + uses procmon-process-attributes-state; + } + } + } + } + + grouping procmon-process-attributes-state { + description + "Attributes state definitions for a process"; + + leaf pid { + type uint64; + description + "The process pid"; + } + + leaf name { + type string; + description + "The process name"; + } + + leaf-list args { + type string; + description + "Current process command line arguments. Arguments with + a parameter (e.g., --option 10 or -option=10) should be + represented as a single element of the list with the + argument name and parameter together. Flag arguments, i.e., + those without a parameter should also be in their own list + element."; + } + + leaf start-time { + type uint64; + units "ns"; + description + "The time at which this process started, + reported as nanoseconds since the UNIX epoch. The + system must be synchronized such that the start-time + can be reported accurately, otherwise it should not be + reported."; + } + + leaf uptime { + type oc-types:timeticks64; + description + "Amount of time elapsed since this process started."; + } + + leaf cpu-usage-user { + type oc-types:timeticks64; + description + "CPU time consumed by this process in user mode."; + } + + leaf cpu-usage-system { + type oc-types:timeticks64; + description + "CPU time consumed by this process in kernel mode."; + } + + leaf cpu-utilization { + type oc-types:percentage; + description + "The percentage of CPU that is being used by the process."; + } + + leaf memory-usage { + type uint64; + units "bytes"; + description + "Bytes allocated and still in use by the process"; + } + + leaf memory-utilization { + type oc-types:percentage; + description + "The percentage of RAM that is being used by the process."; + } + } + + // augment statements + + // rpc statements + + // notification statements +} diff --git a/models/yang/common/openconfig-rib-bgp-attributes.yang b/models/yang/common/openconfig-rib-bgp-attributes.yang new file mode 100644 index 0000000000..61992a2042 --- /dev/null +++ b/models/yang/common/openconfig-rib-bgp-attributes.yang @@ -0,0 +1,926 @@ +submodule openconfig-rib-bgp-attributes { + + belongs-to openconfig-rib-bgp { + prefix "oc-rib-bgp"; + } + + + // import some basic types + import openconfig-bgp-types { prefix oc-bgpt; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-rib-bgp-types { prefix oc-bgprt; } + import openconfig-segment-routing { prefix oc-sr; } + import openconfig-inet-types { prefix oc-inet; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains common data definitions for BGP + attributes for use in BGP RIB tables."; + + + oc-ext:openconfig-version "0.5.0"; + + revision "2019-04-16" { + description + "Rename the top-level BGP RIB container's name + to RIB."; + reference "0.5.0"; + } + + revision "2019-02-27" { + description + "Remove top-level BGP RIB container, and update list + names to be compatible with path compression."; + reference "0.4.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2016-10-17" { + description + "OpenConfig BGP RIB refactor"; + reference "0.3.0"; + } + + grouping bgp-as-path-attr-state { + description + "Data for representing BGP AS-PATH attribute"; + + leaf type { + type oc-bgpt:as-path-segment-type; + description + "The type of AS-PATH segment"; + } + + leaf-list member { + type oc-inet:as-number; + description + "List of the AS numbers in the AS-PATH segment"; + } + } + + grouping bgp-as-path-attr-top { + description + "Top-level grouping for AS-PATH attribute data"; + + container as-path { + description + "Enclosing container for the list of AS path segments. + + In the Adj-RIB-In or Adj-RIB-Out, this list should show + the received or sent AS_PATH, respectively. For + example, if the local router is not 4-byte capable, this + value should consist of 2-octet ASNs or the AS_TRANS + (AS 23456) values received or sent in route updates. + + In the Loc-RIB, this list should reflect the effective + AS path for the route, e.g., a 4-octet value if the + local router is 4-octet capable."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4) + RFC 6793 - BGP Support for Four-octet AS Number Space + RFC 5065 - Autonomous System Confederations for BGP"; + + list as-segment { + description + "Unkeyed list of AS PATH segments"; + + container state { + config false; + description + "Opstate data for AS-PATH segments"; + + uses bgp-as-path-attr-state; + } + } + } + } + + grouping bgp-as4-path-attr-top { + description + "Top-level grouping for AS4-PATH attribute data"; + + container as4-path { + description + "This is the path encoded with 4-octet + AS numbers in the optional transitive AS4_PATH attribute. + This value is populated with the received or sent attribute + in Adj-RIB-In or Adj-RIB-Out, respectively. It should not + be populated in Loc-RIB since the Loc-RIB is expected to + store the effective AS-Path in the as-path leaf regardless + of being 4-octet or 2-octet."; + reference + "RFC 6793 - BGP Support for Four-octet AS Number Space"; + + list as4-segment { + description + "Unkeyed list of AS PATH segments"; + + container state { + config false; + description + "Opstate data for AS-PATH segments"; + + uses bgp-as-path-attr-state; + } + } + } + } + + grouping bgp-community-attr-state { + description + "Common definition of BGP community attributes"; + + leaf-list community { + type union { + type oc-bgpt:bgp-well-known-community-type; + type oc-bgpt:bgp-std-community-type; + } + description + "List of standard or well-known BGP community + attributes."; + } + } + + grouping bgp-extended-community-attr-state { + description + "Common definition of BGP extended community attribute"; + + leaf-list ext-community { + type oc-bgpt:bgp-ext-community-recv-type; + description + "List of BGP extended community attributes. The received + extended community may be an explicitly modeled + type or unknown, represented by an 8-octet value + formatted according to RFC 4360."; + reference + "RFC 4360 - BGP Extended Communities Attribute"; + } + + } + + grouping bgp-aggregator-attr-state { + description + "Operational state data for the BGP aggregator + attribute"; + + leaf as { + type oc-inet:as-number; + description + "AS number of the autnonomous system that performed the + aggregation."; + } + + leaf as4 { + type oc-inet:as-number; + description + "AS number of the autnonomous system that performed the + aggregation (4-octet representation). This value is + populated if an upstream router is not 4-octet capable. + Its semantics are similar to the AS4_PATH optional + transitive attribute"; + reference + "RFC 6793 - BGP Support for Four-octet AS Number Space"; + } + + leaf address { + type oc-inet:ipv4-address; + description + "IP address of the router that performed the + aggregation."; + } + } + + + grouping bgp-aggregator-attr-top { + description + "Common definition of the BGP aggregator attribute"; + + container aggregator { + description + "BGP attribute indicating the prefix has been aggregated by + the specified AS and router."; + + container state { + config false; + description + "Operational state data for BGP aggregator attribute"; + + uses bgp-aggregator-attr-state; + } + } + } + + grouping bgp-shared-common-attr-state { + description + "Route attributes shared across route table entries, + common to both LOC-Rib and Adj-RIB"; + + + leaf origin { + type oc-bgpt:bgp-origin-attr-type; + description + "BGP attribute defining the origin of the path information."; + } + + leaf atomic-aggregate { + type boolean; + description + "BGP attribute indicating that the prefix is an atomic + aggregate, i.e., the peer selected a less specific + route without selecting a more specific route that is + included in it."; + } + + leaf next-hop { + type oc-inet:ip-address; + description + "BGP next hop attribute defining the IP address of the router + that should be used as the next hop to the destination"; + } + + leaf med { + type uint32; + description + "BGP multi-exit discriminator attribute used in BGP route + selection process"; + } + + leaf local-pref { + type uint32; + description + "BGP local preference attribute sent to internal peers to + indicate the degree of preference for externally learned + routes. The route with the highest local preference value + is preferred."; + } + + leaf originator-id { + type oc-inet:ipv4-address; + description + "BGP attribute that provides the id as an IPv4 address + of the originator of the announcement."; + reference + "RFC 4456 - BGP Route Reflection: An Alternative to Full + Mesh Internal BGP (IBGP)"; + } + + leaf-list cluster-list { + type oc-inet:ipv4-address; + description + "Represents the reflection path that the route has passed."; + reference + "RFC 4456 - BGP Route Reflection: An Alternative to Full + Mesh Internal BGP (IBGP)"; + } + + leaf aigp { + type uint64; + description + "BGP path attribute representing the accumulated IGP metric + for the path"; + reference + "RFC 7311 - The Accumulated IGP Metric Attribute for BGP"; + } + } + + grouping bgp-unknown-attr-flags-state { + description + "Operational state data for path attribute flags"; + + leaf optional { + type boolean; + description + "Defines whether the attribute is optional (if + set to true) or well-known (if set to false). + Set in the high-order bit of the BGP attribute + flags octet."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + leaf transitive { + type boolean; + description + "Defines whether an optional attribute is transitive + (if set to true) or non-transitive (if set to false). For + well-known attributes, the transitive flag MUST be set to + true. Set in the second high-order bit of the BGP attribute + flags octet."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + leaf partial { + type boolean; + description + "Defines whether the information contained in the optional + transitive attribute is partial (if set to true) or complete + (if set to false). For well-known attributes and for + optional non-transitive attributes, the partial flag + must be set to false. Set in the third high-order bit of + the BGP attribute flags octet."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + leaf extended { + type boolean; + description + "Defines whether the attribute length is one octet + (if set to false) or two octets (if set to true). Set in + the fourth high-order bit of the BGP attribute flags + octet."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + } + + grouping bgp-unknown-attr-state { + description + "Operational state data for path attributes not shared + across route entries, common to LOC-RIB and Adj-RIB"; + + leaf attr-type { + type uint8; + description + "1-octet value encoding the attribute type code"; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + leaf attr-len { + type uint16; + description + "One or two octet attribute length field indicating the + length of the attribute data in octets. If the Extended + Length attribute flag is set, the length field is 2 octets, + otherwise it is 1 octet"; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + + leaf attr-value { + type binary { + length 1..65535; + } + description + "Raw attribute value, not including the attribute + flags, type, or length. The maximum length + of the attribute value data is 2^16-1 per the max value + of the attr-len field (2 octets)."; + reference + "RFC 4271 - A Border Gateway Protocol 4 (BGP-4)"; + } + } + + grouping bgp-unknown-attr-top { + description + "Unknown path attributes that are not expected to be shared + across route entries, common to LOC-RIB and Adj-RIB"; + + container unknown-attributes { + description + "Unknown path attributes that were received in the UPDATE + message which contained the prefix."; + + list unknown-attribute { + key "attr-type"; + description + "This list contains received attributes that are unrecognized + or unsupported by the local router. The list may be empty."; + + leaf attr-type { + type leafref { + path "../state/attr-type"; + } + description + "Reference to the list key"; + } + + container state { + description + "Operational state for unknown route attributes"; + + uses bgp-unknown-attr-flags-state; + uses bgp-unknown-attr-state; + } + } + } + } + + grouping bgp-loc-rib-attr-state { + description + "Path attributes that are not expected to be shared across + route entries, specific to LOC-RIB"; + + } + + grouping bgp-adj-rib-attr-state { + description + "Path attributes that are not expected to be shared across + route entries, specific to Adj-RIB"; + + leaf path-id { + type uint32; + description + "When the BGP speaker supports advertisement of multiple + paths for a prefix, the path identifier is used to + uniquely identify a route based on the combination of the + prefix and path id. In the Adj-RIB-In, the path-id value is + the value received in the update message. In the Loc-RIB, + if used, it should represent a locally generated path-id + value for the corresponding route. In Adj-RIB-Out, it + should be the value sent to a neighbor when add-paths is + used, i.e., the capability has been negotiated."; + reference + "draft-ietf-idr-add-paths - Advertisement of Multiple Paths + in BGP"; + } + } + + grouping bgp-tunnel-encapsulation-attr-top { + description + "Top-level definition of the BGP Tunnel encapsulation + attribute."; + + container tunnel-encapsulation { + config false; + description + "The Tunnel Encapsulation attribute specifies a set of + tunnels to a remote destination. The attribute is TLV + based and allows description of a tunnel type, and the + relevant information to create the tunnel to the remote + destination."; + + reference "RFC5512, draft-ietf-idr-tunnel-encaps"; + + container tunnels { + description + "Surrounding container for the set of tunnels included + within the tunnel encapsulation attribute."; + + list tunnel { + key "type"; + description + "List of the tunnels that are specified within the + attribute. Keyed on the type of tunnel that the + TLV describes."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the tunnel type specified within the + TLV's type field."; + } + + container state { + config false; + description + "State parameters of the tunnel attribute"; + + uses bgp-tunnel-encapsulation-attr-tunnel-state; + } + + container subtlvs { + description + "Surrounding container for the list of sub-TLVs within + the tunnel encapsulation attribute."; + + list subtlv { + key "type"; + description + "List of the subTLVs that are specified within the + TLV instance inside the tunnel encapsulation attribute."; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to the sub-TLV type that is included within + the subTLV."; + } + + container state { + config false; + description + "State parameters of the subTLV of the tunnel attribute"; + + uses bgp-tunnel-encapsulation-attr-tunnel-subtlv-state; + } + + container remote-endpoints { + when "../state/type = 'oc-bgprt:TUNNEL_REMOTE_ENDPOINT'" { + description + "Only allow the remote endpoint to be specified when the + subTLV is specified to describe remote endpoints."; + } + + description + "The remote endpoints associated with the tunnel + described by the attribute."; + + list remote-endpoint { + key "endpoint"; + description + "List of the remote endpoints described within the TLV."; + + leaf endpoint { + type leafref { + path "../state/endpoint"; + } + description + "Reference to the IP address of the endpoint."; + } + + container state { + config false; + description + "State parameters of the remote endpoints described + by the attribute."; + + uses bgp-tunnel-encapsulation-attr-tunnel-subtlv-endpoint-state; + } + } + } + + container segment-lists { + when "../state/type = 'oc-bgprt:SRTE_SEGMENT_LIST'" { + description + "Only allow the segment lists to be specified when the sub-TLV + is of the relevant type."; + } + + description + "Surrounding container for the list of segment lists that are + associated with a SR-TE Policy tunnel."; + + list segment-list { + key "instance-id"; + + description + "List of segment lists that are specified within the + tunnel encapsulation attribute."; + + leaf instance-id { + type leafref { + path "../state/instance-id"; + } + description + "Reference to the instance identifier of the Segment List + that is included within the tunnel encapsulation + attribute."; + } + + container state { + config false; + description + "State parameters relating to the Segment List within the + Tunnel Encapsulation attribute."; + + uses bgp-tunnel-encapsulation-attr-tunnel-subtlv-segment-list-state; + } + + container segments { + description + "Surrounding container for the list of segments within the + SR-TE segment list."; + + list segment { + key "index"; + + description + "List of segments within the SR-TE segment list."; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to the index of the segment within the + segment list."; + } + + container state { + config false; + description + "State parameters relating to the segment within + the segment list."; + + uses bgp-tunnel-encapsulation-attr-tunnel-subtlv-segment-state; + } + } + } + } + } + } + } + } + } + } + } + + grouping bgp-tunnel-encapsulation-attr-tunnel-state { + description + "State parameters of the tunnel encapsulation attribute"; + + leaf type { + type identityref { + base "oc-bgprt:TUNNEL_ENCAPSULATION_TYPE"; + } + description + "Type of the tunnel described within the tunnel encapsulation + attribute."; + } + } + + grouping bgp-tunnel-encapsulation-attr-tunnel-subtlv-state { + description + "State parameters relating to subTLVs of the tunnel encapsulation + attribute."; + + leaf type { + type identityref { + base "oc-bgprt:TUNNEL_ENCAPSULATION_SUBTLV_TYPE"; + } + description + "Type of the sub-TLV within the tunnel encapsulation attribute"; + } + + leaf-list colors { + when "../type = 'oc-bgprt:TUNNEL_COLOR'" { + description + "Only allow list of colours to be specified when the sub-TLV + specifies colours associated with the tunnel encapsulation + attribute."; + } + type uint32; + description + "The colours associated with the tunnel encapsulation attribute, + as described by RFC5512."; + } + + leaf preference { + when "../type = 'oc-bgprt:SRTE_PREFERENCE'" { + description + "Only allow the preference to be specified when the sub-TLV + specifies the preference associated with the tunnel encapsulation + attribute."; + } + type uint32; + default 100; + description + "The preference of the SR-TE policy described by the tunnel + encapsulation attribute. If unspecified, the preference + defaults to 100."; + } + + leaf binding-sid { + when "../type = 'oc-bgprt:SRTE_BINDING_SID'" { + description + "Only allow the binding SID to be specified when the sub-TLV + is specified to be the of the relevant type."; + } + type oc-sr:sr-sid-type; + description + "Binding SID associated with the SR-TE policy"; + } + } + + grouping bgp-tunnel-encapsulation-attr-tunnel-subtlv-endpoint-state { + description + "State parameters relating to the remote endpoint described by a + tunnel encapsulation attribute."; + + leaf as { + type oc-inet:as-number; + description + "The remote AS to which the IP address of the remote endpoint + belongs."; + } + + leaf endpoint { + type oc-inet:ip-address; + description + "IP address of the remote endpoint."; + } + } + + grouping bgp-tunnel-encapsulation-attr-tunnel-subtlv-segment-list-state { + description + "State parameters relating to an entry within a segment list within + a SR-TE policy segment list."; + + leaf instance-id { + type uint64; + description + "Instance of the segment list within the sub-TLV"; + } + + leaf weight { + type uint32; + description + "The weight given to the path within the set of segment + lists that are included in the tunnel attribute sub-TLV."; + } + } + + grouping bgp-tunnel-encapsulation-attr-tunnel-subtlv-segment-state { + description + "State parameters relating to a segment within the segment list."; + + leaf index { + type uint64; + description + "Index of the segment within the segment list. The segments are + ordered in ascending order, beginning at 0."; + } + + leaf type { + type enumeration { + enum MPLS_SID { + description + "The segment is specified as an MPLS label."; + value 1; + } + enum IPV6_SID { + description + "The segment is specified as an IPv6 address."; + value 2; + } + enum IPV4_NODE_ADDRESS { + description + "The segment is specified as an IPv4 node address with + optional SID."; + value 3; + } + enum IPV6_NODE_ADDRESS { + description + "The segment is specified as an IPv6 node address with + optional SID."; + value 4; + } + enum IPV4_LOCAL_INTF_ID { + description + "The segment is specified as an IPv4 address with a + local interface identifier along with an ."; + value 5; + } + enum IPV4_LOCAL_REMOTE_ADDR { + description + "The segment is specified as an IPv4 local and remote + address with an optional SID."; + value 6; + } + enum IPV6_LOCAL_INTF_ID { + description + "The segment is specified as an IPv6 address with an + index, along with an optional SID."; + value 7; + } + enum IPV6_LOCAL_REMOTE_ADDR { + description + "The segmetn is specified as an IPv6 local and remote + address with an optional SID."; + value 8; + } + } + description + "The type of segment specified within the segment entry."; + } + + leaf sid { + type oc-sr:sr-sid-type; + description + "SID value for the segment entry, specified as an MPLS label + or IPv6 address."; + } + + leaf mpls-tc { + when "../type = 'MPLS_SID'" { + description + "The MPLS TC bits can only be specified when the segment + time is an MPLS label."; + } + type uint8 { + range "0..7"; + } + description + "The MPLS TC bits used when the SID is specified as an MPLS + label. If set to zero, the receiving system specifies the + value of the TC bits."; + } + + leaf mpls-bos { + when "../type = 'MPLS_SID'" { + description + "The MPLS BoS bit can only be specified when the segment + type is an MPLS label."; + } + type boolean; + description + "When this leaf is set to true the MPLS bottom-of-stack + (BoS) bit is set in the MPLS segment. The BoS bit should + always be set to zero by the sender."; + } + + leaf mpls-ttl { + when "../type = 'MPLS_SID'" { + description + "The MPLS TTL can only be set when the segment type is + an MPLS label."; + } + type uint8; + description + "The MPLS time to live (TTL) to be set for the MPLS + segment. If set to 255, the receiver specifies the + TTL value that is used for packets sent with this + segment in the stack."; + } + + leaf remote-ipv4-address { + when "../type = 'IPV4_NODE_ADDRESS' or ../type='../IPV4_ADDRESS_INDEX'" + + "or ../type='IPV4_LOCAL_INTF_ID' or " + + "../type='IPV4_LOCAL_REMOTE_ADDR'" { + description + "An IPv4 address can only be associated with the segment entry + when the type of the SID is a node address, or an IPv6 address + with an index."; + } + type oc-inet:ipv4-address; + description + "An IPv4 address specified as the remote node address. When the type + of the segment specifies only the remote address, no other addresses + are specified. When the type of the segment requires a local address, + this leaf specifies the remote IPv4 address."; + } + + leaf local-ipv4-address { + when "../type = 'IPV4_LOCAL_REMOTE_ADDR'" { + description + "A local IPv4 address can only be specified when the segment is + specified by the local and remote IPv4 interface addresses."; + } + type oc-inet:ipv4-address; + description + "An IPv4 address of a local adjacency that is used to identify + the segment."; + } + + leaf remote-ipv6-address { + when "../type = 'IPV6_NODE_ADDRESS' or ../type='IPV6_ADDRESS_INDEX'" + + "or ../type='IPV6_LOCAL_INTF_ID' or " + + "../type='IPV6_LOCAL_REMOTE_ADDR'" { + description + "An IPv6 address can only be specified with a segment entry + when the type of the SID is a node address, or an IPv6 address + with an index."; + } + type oc-inet:ipv6-address; + description + "An IPv6 address specified as the remote node address. When the type + of the segment specifies only the remote address, no other addresses + are specified. When the type of the segment requires a local address, + this leaf specifies the remote IPv6 address."; + } + + leaf local-ipv6-address { + when "../type = 'IPV6_LOCAL_REMOTE_ADDR'" { + description + "A local IPv6 address can only be speciifed when the segment + is specified by the local and remote IPv6 interface addresses."; + } + type oc-inet:ipv6-address; + description + "An IPv6 address of a local adjacency that is used to identify the + segment."; + } + + leaf local-interface-id { + when "../type = 'IPV4_LOCAL_INTF_ID' or ../type='IPV6_LOCAL_INTF_ID'" { + description + "A local interface identifier can only be specified when the + type of the segment is an IPv4 address with local interface ID, + or IPv6 address with local interface ID."; + } + type uint32; + description + "The local interface identifier to be utilised for the segment."; + reference + "draft-ietf-pce-segment-routing"; + } + } +} diff --git a/models/yang/common/openconfig-rib-bgp-shared-attributes.yang b/models/yang/common/openconfig-rib-bgp-shared-attributes.yang new file mode 100644 index 0000000000..8fa2a8d082 --- /dev/null +++ b/models/yang/common/openconfig-rib-bgp-shared-attributes.yang @@ -0,0 +1,190 @@ +submodule openconfig-rib-bgp-shared-attributes { + + belongs-to openconfig-rib-bgp { + prefix "oc-rib-bgp"; + } + + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + include openconfig-rib-bgp-attributes; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains structural data definitions for + attribute sets shared across routes."; + + oc-ext:openconfig-version "0.5.0"; + + revision "2019-04-16" { + description + "Rename the top-level BGP RIB container's name + to RIB."; + reference "0.5.0"; + } + + revision "2019-02-27" { + description + "Remove top-level BGP RIB container, and update list + names to be compatible with path compression."; + reference "0.4.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2016-10-17" { + description + "OpenConfig BGP RIB refactor"; + reference "0.3.0"; + } + + + grouping attribute-sets-top { + description + "Top level grouping for list of common attribute sets"; + + container attr-sets { + description + "Enclosing container for the list of path attribute sets"; + + list attr-set { + key "index"; + + description + "List of path attributes that may be in use by multiple + routes in the table"; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to list key"; + } + + container state { + config false; + description + "Operational state for common path attributes"; + + leaf index { + type uint64; + description + "System generated index for each attribute set. The + index is used to reference an attribute set from a + specific path. Multiple paths may reference the same + attribute set."; + } + + uses bgp-shared-common-attr-state; + } + uses bgp-aggregator-attr-top; + uses bgp-as-path-attr-top; + uses bgp-as4-path-attr-top; + uses bgp-tunnel-encapsulation-attr-top; + } + } + } + + grouping community-sets-top { + description + "Top level grouping for list of shared community attribute + sets"; + + container communities { + description + "Enclosing container for the list of community attribute + sets"; + + list community { + key "index"; + + description + "List of path attributes that may be in use by multiple + routes in the table"; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to the list key"; + } + + container state { + config false; + description + "Operational state for shared BGP community attribute"; + + leaf index { + type uint64; + description + "System generated index for each attribute set. The + index is used to reference an attribute set from a + specific path. Multiple paths may reference the same + attribute set."; + } + + uses bgp-community-attr-state; + } + } + } + } + + grouping ext-community-sets-top { + description + "Top level grouping for list of extended community attribute + sets"; + + container ext-communities { + description + "Enclosing container for the list of extended community + attribute sets"; + + list ext-community { + key "index"; + + description + "List of path attributes that may be in use by multiple + routes in the table"; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to the list key"; + } + + container state { + config false; + description + "Operational state for shared BGP extended community + attribute"; + + leaf index { + type uint64; + description + "System generated index for each attribute set. The + index is used to reference an attribute set from a + specific path. Multiple paths may reference the same + attribute set."; + } + + uses bgp-extended-community-attr-state; + } + } + } + } +} diff --git a/models/yang/common/openconfig-rib-bgp-table-attributes.yang b/models/yang/common/openconfig-rib-bgp-table-attributes.yang new file mode 100644 index 0000000000..205b930519 --- /dev/null +++ b/models/yang/common/openconfig-rib-bgp-table-attributes.yang @@ -0,0 +1,132 @@ +submodule openconfig-rib-bgp-table-attributes { + + belongs-to openconfig-rib-bgp { + prefix "oc-rib-bgp"; + } + + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + import openconfig-types { prefix oc-types; } + import openconfig-rib-bgp-types { prefix oc-bgpribt; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains common data definitions for data + related to a RIB entry, or RIB table."; + + oc-ext:openconfig-version "0.5.0"; + + revision "2019-04-16" { + description + "Rename the top-level BGP RIB container's name + to RIB."; + reference "0.5.0"; + } + + revision "2019-02-27" { + description + "Remove top-level BGP RIB container, and update list + names to be compatible with path compression."; + reference "0.4.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2016-10-17" { + description + "OpenConfig BGP RIB refactor"; + reference "0.3.0"; + } + + + + grouping bgp-common-route-annotations-state { + description + "Data definitions for flags and other information attached + to routes in both LOC-RIB and Adj-RIB"; + + leaf last-modified { + type oc-types:timeticks64; + description + "Timestamp when this path was last modified. + + The value is the timestamp in seconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + leaf valid-route { + type boolean; + description + "Indicates that the route is considered valid by the + local router"; + } + + leaf invalid-reason { + type identityref { + base oc-bgpribt:INVALID_ROUTE_REASON; + } + description + "If the route is rejected as invalid, this indicates the + reason."; + } + + } + + grouping bgp-loc-rib-route-annotations-state { + description + "Data definitions for information attached to routes in the + LOC-RIB"; + + // placeholder for route metadata specific to the LOC-RIB + + } + + grouping bgp-adj-rib-in-post-route-annotations-state { + description + "Data definitions for information attached to routes in the + Adj-RIB-in post-policy table"; + + leaf best-path { + type boolean; + description + "Current path was selected as the best path."; + } + } + + grouping bgp-common-table-attrs-state { + description + "Common attributes attached to all routing tables"; + + // placeholder for metadata associated with all tables + } + + grouping bgp-common-table-attrs-top { + description + "Operational state data for common attributes attached to + all routing tables"; + // no enclosing container as this data will fit under an + // existing LOC-RIB container + + container state { + config false; + description + "Operational state data for data related to the entire + LOC-RIB"; + + uses bgp-common-table-attrs-state; + } + } + + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-rib-bgp-tables.yang b/models/yang/common/openconfig-rib-bgp-tables.yang new file mode 100644 index 0000000000..7e0bf6c5d5 --- /dev/null +++ b/models/yang/common/openconfig-rib-bgp-tables.yang @@ -0,0 +1,903 @@ +submodule openconfig-rib-bgp-tables { + + belongs-to openconfig-rib-bgp { + prefix "oc-rib-bgp"; + } + + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-policy-types { prefix oc-pol-types; } + + include openconfig-rib-bgp-attributes; + include openconfig-rib-bgp-shared-attributes; + include openconfig-rib-bgp-table-attributes; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This submodule contains structural data definitions for + BGP routing tables."; + + oc-ext:openconfig-version "0.5.0"; + + revision "2019-04-16" { + description + "Rename the top-level BGP RIB container's name + to RIB."; + reference "0.5.0"; + } + + revision "2019-02-27" { + description + "Remove top-level BGP RIB container, and update list + names to be compatible with path compression."; + reference "0.4.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2016-10-17" { + description + "OpenConfig BGP RIB refactor"; + reference "0.3.0"; + } + + + grouping bgp-adj-rib-common-attr-refs { + description + "Definitions of common references to attribute sets for + multiple AFI-SAFIs for Adj-RIB tables"; + + leaf attr-index { + type leafref { + path "../../../../../../../../../../attr-sets/attr-set/" + + "state/index"; + } + description + "Reference to the common attribute group for the + route"; + } + + leaf community-index { + type leafref { + path "../../../../../../../../../../communities/community/" + + "state/index"; + } + description + "Reference to the community attribute for the route"; + } + + leaf ext-community-index { + type leafref { + path "../../../../../../../../../../ext-communities/" + + "ext-community/state/index"; + } + description + "Reference to the extended community attribute for the + route"; + } + } + + grouping bgp-loc-rib-common-attr-refs { + description + "Definitions of common references to attribute sets for + multiple AFI-SAFIs for LOC-RIB tables"; + + leaf attr-index { + type leafref { + path "../../../../../../../../attr-sets/attr-set/" + + "state/index"; + } + description + "Reference to the common attribute group for the + route"; + } + + leaf community-index { + type leafref { + path "../../../../../../../../communities/community/" + + "state/index"; + } + description + "Reference to the community attribute for the route"; + } + + leaf ext-community-index { + type leafref { + path "../../../../../../../../ext-communities/" + + "ext-community/state/index"; + } + description + "Reference to the extended community attribute for the + route"; + } + } + + grouping bgp-loc-rib-common-keys { + description + "Common references used in keys for IPv4 and IPv6 + LOC-RIB entries"; + + leaf origin { + type union { + type oc-inet:ip-address; + type identityref { + base oc-pol-types:INSTALL_PROTOCOL_TYPE; + } + } + description + "Indicates the origin of the route. If the route is learned + from a neighbor, this value is the neighbor address. If + the route was injected or redistributed from another + protocol, the origin indicates the source protocol for the + route."; + } + + leaf path-id { + type uint32; + default 0; + description + "If the route is learned from a neighbor, the path-id + corresponds to the path-id for the route in the + corresponding adj-rib-in-post table. If the route is + injected from another protocol, or the neighbor does not + support BGP add-paths, the path-id should be set + to zero, also the default value."; + } + } + + grouping bgp-loc-rib-key-refs { + description + "Key references to support operational state structure for + the BGP LOC-RIB table"; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the prefix list key"; + } + + leaf origin { + type leafref { + path "../state/origin"; + } + description + "Reference to the origin list key"; + } + + leaf path-id { + type leafref { + path "../state/path-id"; + } + description + "Reference to the path-id list key"; + } + } + + grouping ipv4-loc-rib-top { + description + "Top-level grouping for IPv4 routing tables"; + + container loc-rib { + config false; + description + "Container for the IPv4 BGP LOC-RIB data"; + + uses bgp-common-table-attrs-top; + + container routes { + description + "Enclosing container for list of routes in the routing + table."; + + list route { + key "prefix origin path-id"; + + description + "List of routes in the table, keyed by the route + prefix, the route origin, and path-id. The route + origin can be either the neighbor address from which + the route was learned, or the source protocol that + injected the route. The path-id distinguishes routes + for the same prefix received from a neighbor (e.g., + if add-paths is eanbled)."; + + uses bgp-loc-rib-key-refs; + + container state { + description + "Operational state data for route entries in the + BGP LOC-RIB"; + + leaf prefix { + type oc-inet:ipv4-prefix; + description + "The IPv4 prefix corresponding to the route"; + } + + uses bgp-loc-rib-common-keys; + uses bgp-loc-rib-common-attr-refs; + uses bgp-loc-rib-attr-state; + uses bgp-common-route-annotations-state; + uses bgp-loc-rib-route-annotations-state; + + } + + uses bgp-unknown-attr-top; + + } + } + } + } + + grouping ipv6-loc-rib-top { + description + "Top-level grouping for IPv6 routing tables"; + + container loc-rib { + config false; + description + "Container for the IPv6 BGP LOC-RIB data"; + + uses bgp-common-table-attrs-top; + + container routes { + description + "Enclosing container for list of routes in the routing + table."; + + list route { + key "prefix origin path-id"; + + description + "List of routes in the table, keyed by the route + prefix, the route origin, and path-id. The route + origin can be either the neighbor address from which + the route was learned, or the source protocol that + injected the route. The path-id distinguishes routes + for the same prefix received from a neighbor (e.g., + if add-paths is eanbled)."; + + uses bgp-loc-rib-key-refs; + + container state { + description + "Operational state data for route entries in the + BGP LOC-RIB"; + + leaf prefix { + type oc-inet:ipv6-prefix; + description + "The IPv6 prefix corresponding to the route"; + } + + uses bgp-loc-rib-common-keys; + uses bgp-loc-rib-common-attr-refs; + uses bgp-loc-rib-attr-state; + uses bgp-common-route-annotations-state; + uses bgp-loc-rib-route-annotations-state; + + } + + uses bgp-unknown-attr-top; + } + } + } + } + + grouping bgp-adj-rib-key-refs { + description + "Key references to support operational state structure for + the BGP Adj-RIB tables"; + + leaf prefix { + type leafref { + path "../state/prefix"; + } + description + "Reference to the prefix list key"; + } + + leaf path-id { + type leafref { + path "../state/path-id"; + } + description + "Reference to the path-id list key"; + } + } + + grouping ipv4-adj-rib-common { + description + "Common structural grouping for each IPv4 adj-RIB table"; + + uses bgp-common-table-attrs-top; + + container routes { + config false; + description + "Enclosing container for list of routes in the routing + table."; + + list route { + key "prefix path-id"; + + description + "List of routes in the table, keyed by a combination of + the route prefix and path-id to distinguish multiple + routes received from a neighbor for the same prefix, + e.g., when BGP add-paths is enabled."; + + uses bgp-adj-rib-key-refs; + + container state { + description + "Operational state data for BGP Adj-RIB entries"; + + leaf prefix { + type oc-inet:ipv4-prefix; + description + "Prefix for the route"; + } + + uses bgp-adj-rib-attr-state; + uses bgp-adj-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + } + + uses bgp-unknown-attr-top; + + } + } + } + + grouping ipv4-adj-rib-in-post { + description + "Common structural grouping for the IPv4 adj-rib-in + post-policy table"; + + uses bgp-common-table-attrs-top; + + container routes { + config false; + description + "Enclosing container for list of routes in the routing + table."; + + list route { + key "prefix path-id"; + + description + "List of routes in the table, keyed by a combination of + the route prefix and path-id to distinguish multiple + routes received from a neighbor for the same prefix, + e.g., when BGP add-paths is enabled."; + + uses bgp-adj-rib-key-refs; + + container state { + description + "Operational state data for BGP Adj-RIB entries"; + + leaf prefix { + type oc-inet:ipv4-prefix; + description + "Prefix for the route"; + } + + uses bgp-adj-rib-attr-state; + uses bgp-adj-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + uses bgp-adj-rib-in-post-route-annotations-state; + } + + uses bgp-unknown-attr-top; + } + } + } + + + grouping ipv4-adj-rib-top { + description + "Top-level grouping for Adj-RIB table"; + + container neighbors { + config false; + description + "Enclosing container for neighbor list"; + + list neighbor { + key "neighbor-address"; + description + "List of neighbors (peers) of the local BGP speaker"; + + leaf neighbor-address { + type leafref { + path "../state/neighbor-address"; + } + description + "Reference to the list key"; + } + + container state { + description + "Operational state for each neighbor BGP Adj-RIB"; + + leaf neighbor-address { + type oc-inet:ip-address; + description + "IP address of the BGP neighbor or peer"; + } + } + + container adj-rib-in-pre { + description + "Per-neighbor table containing the NLRI updates + received from the neighbor before any local input + policy rules or filters have been applied. This can + be considered the 'raw' updates from the neighbor."; + + uses ipv4-adj-rib-common; + + } + + container adj-rib-in-post { + description + "Per-neighbor table containing the paths received from + the neighbor that are eligible for best-path selection + after local input policy rules have been applied."; + + uses ipv4-adj-rib-in-post; + } + + container adj-rib-out-pre { + description + "Per-neighbor table containing paths eligble for + sending (advertising) to the neighbor before output + policy rules have been applied"; + + uses ipv4-adj-rib-common; + + } + + container adj-rib-out-post { + description + "Per-neighbor table containing paths eligble for + sending (advertising) to the neighbor after output + policy rules have been applied"; + + uses ipv4-adj-rib-common; + + } + } + } + } + + grouping ipv6-adj-rib-common { + description + "Common structural grouping for each IPv6 adj-RIB table"; + + uses bgp-common-table-attrs-state; + + container routes { + config false; + description + "Enclosing container for list of routes in the routing + table."; + + list route { + key "prefix path-id"; + + description + "List of routes in the table"; + + uses bgp-adj-rib-key-refs; + + container state { + description + "Operational state data for BGP Adj-RIB entries"; + + leaf prefix { + type oc-inet:ipv6-prefix; + description + "Prefix for the route"; + } + + uses bgp-adj-rib-attr-state; + uses bgp-adj-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + } + + uses bgp-unknown-attr-top; + } + } + } + + grouping ipv6-adj-rib-in-post { + description + "Common structural grouping for the IPv6 adj-rib-in + post-policy table"; + + uses bgp-common-table-attrs-state; + + container routes { + config false; + description + "Enclosing container for list of routes in the routing + table."; + + list route { + key "prefix path-id"; + + description + "List of routes in the table"; + + uses bgp-adj-rib-key-refs; + + container state { + description + "Operational state data for BGP Adj-RIB entries"; + + leaf prefix { + type oc-inet:ipv6-prefix; + description + "Prefix for the route"; + } + + uses bgp-adj-rib-attr-state; + uses bgp-adj-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + uses bgp-adj-rib-in-post-route-annotations-state; + } + + uses bgp-unknown-attr-top; + } + } + } + + grouping ipv6-adj-rib-top { + description + "Top-level grouping for Adj-RIB table"; + + container neighbors { + config false; + description + "Enclosing container for neighbor list"; + + list neighbor { + key "neighbor-address"; + description + "List of neighbors (peers) of the local BGP speaker"; + + leaf neighbor-address { + type leafref { + path "../state/neighbor-address"; + } + description + "Reference to the list key"; + } + + container state { + description + "Operational state for each neighbor BGP Adj-RIB"; + + leaf neighbor-address { + type oc-inet:ip-address; + description + "IP address of the BGP neighbor or peer"; + } + } + + container adj-rib-in-pre { + description + "Per-neighbor table containing the NLRI updates + received from the neighbor before any local input + policy rules or filters have been applied. This can + be considered the 'raw' updates from the neighbor."; + + uses ipv6-adj-rib-common; + + } + + container adj-rib-in-post { + description + "Per-neighbor table containing the paths received from + the neighbor that are eligible for best-path selection + after local input policy rules have been applied."; + + uses ipv6-adj-rib-in-post; + } + + container adj-rib-out-pre { + description + "Per-neighbor table containing paths eligble for + sending (advertising) to the neighbor before output + policy rules have been applied"; + + uses ipv6-adj-rib-common; + + } + + container adj-rib-out-post { + description + "Per-neighbor table containing paths eligble for + sending (advertising) to the neighbor after output + policy rules have been applied"; + + uses ipv6-adj-rib-common; + + } + } + } + } + + grouping ipvX-srte-policy-adjrib-top { + description + "Top-level grouping for the IPv4 and IPv6 AFI, SR-TE Policy SAFI + Adj-RIBs."; + + container neighbors { + description + "Surrounding container for the list of neighbours that are + enabled for the IPv4 and IPv6 AFI, SR-TE Policy SAFI address + family."; + + list neighbor { + key "neighbor-address"; + + description + "An individual neighbour that is enabled for the SR-TE + Policy SAFI."; + + leaf neighbor-address { + type leafref { + path "../state/neighbor-address"; + } + description + "Reference to the address of the neighbour for which the + Adj-RIBs specified are maintained."; + } + + container state { + description + "Operational state parameters of the BGP neighbour for + which the SR-TE Policy SAFI is enabled."; + uses ipvX-srte-policy-adjrib-neighbor-state; + } + + container adj-rib-in-pre { + description + "The Adj-RIB-In for the SR-TE Policy SAFI for the neighbour, + prior to any inbound policy constraints or modifications + having been applied."; + uses ipvX-srte-policy-adjrib-common; + } + + container adj-rib-in-post { + description + "The Adj-RIB-In for the SR-TE Policy SAFI for the neighbour, + following any inbound policy constraints or modifications + being made."; + uses ipvX-srte-policy-adjrib-in-post; + } + + container adj-rib-out-pre { + description + "The Adj-RIB-Out for the SR-TE Policy SAFI for the neighbour, + prior to any outgoing policy modifications or constraints + having been applied."; + uses ipvX-srte-policy-adjrib-common; + } + + container adj-rib-out-post { + description + "The Adj-RIB-Out for the SR-TE Policy SAFI for the neighbour, + follow any outbound policy constraints or modifications being + made."; + uses ipvX-srte-policy-adjrib-common; + } + } + } + } + + grouping ipvX-srte-policy-adjrib-neighbor-state { + description + "Common attributes for each neighbour for which the SR-TE + Policy SAFI RIBs are being maintained."; + + leaf neighbor-address { + description + "The address of the neighbour for which the SR-TE policy + SAFI has been negotiated."; + type oc-inet:ip-address; + } + } + + grouping ipvX-srte-policy-adjrib-common { + description + "Common structure containing the routes that are learnt via + the IPv4 or IPv6 SR-TE Policy SAFI."; + + container routes { + description + "Surrounding container for the list of routes within the + SR-TE Policy SAFI."; + + list route { + key "path-id endpoint color"; + + description + "The routes within the SR-TE Policy SAFI Adj-RIB. The + routes are keyed on the path-id - set to a non-zero + value only if ADD-PATHS is being used; the color; and + the endpoint. The colour and endpoint are extracted from + the NLRI."; + + uses ipvX-srte-policy-common-keys; + + container state { + description + "State parameters for entries within the Adj-RIB used + to store SR-TE Policy SAFI routes."; + + uses ipvX-srte-policy-common-route-state; + uses bgp-adj-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + } + + uses bgp-unknown-attr-top; + } + } + } + + grouping ipvX-srte-policy-common-route-state { + description + "Common attributes used SR-TE Policy SAFI routes."; + + leaf path-id { + type uint32; + default 0; + description + "Identifier for the path when using BGP ADD-PATHS for the SR-TE + policy SAFI."; + } + + leaf endpoint { + type oc-inet:ip-address; + description + "A unique identifier for the remote set of nodes. When the address + family is IPv4, the value is a 4-octet IPv4 address. When the + address family is IPv6, the value is a 16-octet IPv6 address."; + } + + leaf color { + type uint32; + description + "A 4-octet value identifying the policy. Combined with the endpoint + the endpoint and colour represent the unique policy."; + } + } + + grouping ipvX-srte-policy-common-keys { + description + "Common grouping of the keys used for lists of SR-TE policy + SAFI routes."; + + leaf path-id { + type leafref { + path "../state/path-id"; + } + description + "Reference to the path identifier for the SR-TE Policy SAFI + route. The value is only non-zero if ADD-PATHS is not being + used."; + } + + leaf endpoint { + type leafref { + path "../state/endpoint"; + } + description + "Reference to the endpoint used for the SR-TE Policy SAFI + route within the NLRI."; + } + + leaf color { + type leafref { + path "../state/color"; + } + description + "Reference to the colour used for the SR-TE policy SAFI + route within the NLRI."; + } + } + + grouping ipvX-srte-policy-adjrib-in-post { + description + "Grouping for the post-policy Adj-RIB-In for SR-TE Policy SAFI routes"; + + container routes { + description + "The set of routes that are within the Adj-RIB-Out for the + neighbour."; + + list route { + key "path-id endpoint color"; + + description + "The routes that are in the Adj-RIB-In-Post for the specified + BGP neighbour within the SR-TE Policy SAFI for the specified + address family."; + + uses ipvX-srte-policy-common-keys; + + container state { + description + "Operational state attributes related to the route within + the SR-TE Policy SAFI Adj-RIB-In-Post for the specified + neighbour."; + + uses ipvX-srte-policy-common-route-state; + uses bgp-adj-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + uses bgp-adj-rib-in-post-route-annotations-state; + } + + uses bgp-unknown-attr-top; + } + } + } + + grouping ipvX-srte-policy-locrib-top { + description + "Top-level grouping for the Loc-RIB for IPv4 or IPv6 Adj-RIB + for SR-TE Policy SAFI."; + + container loc-rib { + description + "The Loc-RIB for the SR-TE Policy SAFI for IPv4 or IPv6 Unicast + AFIs."; + + container routes { + description + "List of routes within the SR-TE Policy SAFI, for the IPv4 or + IPv6 AFI."; + + list route { + key "path-id endpoint color"; + + description + "Route within the specified address family for the SR-TE + Policy SAFI."; + + uses ipvX-srte-policy-common-keys; + + container state { + description + "Operational state attributes for each route within the + IPv4 or IPv6 Unicast SR-TE Policy SAFI."; + + uses ipvX-srte-policy-common-route-state; + uses bgp-loc-rib-common-attr-refs; + uses bgp-common-route-annotations-state; + } + + uses bgp-unknown-attr-top; + } + } + } + } +} diff --git a/models/yang/common/openconfig-rib-bgp-types.yang b/models/yang/common/openconfig-rib-bgp-types.yang new file mode 100644 index 0000000000..3fcd65d8fa --- /dev/null +++ b/models/yang/common/openconfig-rib-bgp-types.yang @@ -0,0 +1,262 @@ +module openconfig-rib-bgp-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/rib/bgp-types"; + + prefix "oc-bgprib-types"; + + import openconfig-extensions { prefix oc-ext; } + + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Defines identity and type defintions associated with + the OpenConfig BGP RIB modules"; + + oc-ext:openconfig-version "0.4.0"; + + revision "2019-02-27" { + description + "Remove top-level BGP RIB container, and update list + names to be compatible with path compression."; + reference "0.4.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2016-10-17" { + description + "OpenConfig BGP RIB refactor"; + reference "0.3.0"; + } + + revision "2016-04-11" { + description + "OpenConfig public release"; + reference "0.2.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + identity INVALID_ROUTE_REASON { + description + "Base identity for reason code for routes that are rejected as + invalid. Some derived entities are based on BMP v3"; + reference "BGP Monitoring Protocol (draft-ietf-grow-bmp-07)"; + } + + identity INVALID_CLUSTER_LOOP { + base INVALID_ROUTE_REASON; + description + "Route was invalid due to CLUSTER_LIST loop"; + } + + identity INVALID_AS_LOOP { + base INVALID_ROUTE_REASON; + description + "Route was invalid due to AS_PATH loop"; + } + + identity INVALID_ORIGINATOR { + base INVALID_ROUTE_REASON; + description + "Route was invalid due to ORIGINATOR_ID, e.g., update has + local router as originator"; + } + + identity INVALID_CONFED { + base INVALID_ROUTE_REASON; + description + "Route was invalid due to a loop in the AS_CONFED_SEQUENCE or + AS_CONFED_SET attributes"; + } + + identity BGP_NOT_SELECTED_BESTPATH { + description + "Base identity for indicating reason a route was was not + selected by BGP route selection algorithm"; + reference + "RFC 4271 - Section 9.1"; + } + + identity LOCAL_PREF_LOWER { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route has a lower localpref attribute than current best path"; + reference + "RFC 4271 - Section 9.1.2"; + } + + identity AS_PATH_LONGER { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route has a longer AS path attribute than current best path"; + reference + "RFC 4271 - Section 9.1.2.2 (a)"; + } + + identity ORIGIN_TYPE_HIGHER { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route has a higher origin type, i.e., IGP origin is preferred + over EGP or incomplete"; + reference + "RFC 4271 - Section 9.1.2.2 (b)"; + } + + identity MED_HIGHER { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route has a higher MED, or metric, attribute than the current + best path"; + reference + "RFC 4271 - Section 9.1.2.2 (c)"; + } + + identity PREFER_EXTERNAL { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route source is via IGP, rather than EGP."; + reference + "RFC 4271 - Section 9.1.2.2 (d)"; + } + + identity NEXTHOP_COST_HIGHER { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route has a higher interior cost to the next hop."; + reference + "RFC 4271 - Section 9.1.2.2 (e)"; + } + + identity HIGHER_ROUTER_ID { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route was sent by a peer with a higher BGP Identifier value, + or router id"; + reference + "RFC 4271 - Section 9.1.2.2 (f)"; + } + + identity HIGHER_PEER_ADDRESS { + base BGP_NOT_SELECTED_BESTPATH; + description + "Route was sent by a peer with a higher IP address"; + reference + "RFC 4271 - Section 9.1.2.2 (g)"; + } + + identity BGP_NOT_SELECTED_POLICY { + description + "Base identity for reason code for routes that are rejected + due to policy"; + } + + identity REJECTED_IMPORT_POLICY { + base BGP_NOT_SELECTED_POLICY; + description + "Route was rejected after apply import policies"; + } + + identity TUNNEL_ENCAPSULATION_TYPE { + description + "Types of tunnel encapsulation, as described by the Tunnel + Encapsulation attribute"; + reference + "RFC5512"; + } + + identity SRTE_POLICY_TUNNEL { + base TUNNEL_ENCAPSULATION_TYPE; + description + "Segment Routing Traffic Engineering Policy tunnel."; + reference + "draft-previdi-idr-segment-routing-te-policy"; + } + + identity TUNNEL_ENCAPSULATION_SUBTLV_TYPE { + description + "SubTLVs of the Tunnel Encapsulation attribute"; + reference + "RFC5512"; + } + + identity TUNNEL_REMOTE_ENDPOINT { + base TUNNEL_ENCAPSULATION_SUBTLV_TYPE; + description + "Remote endpoint of the tunnel."; + reference + "RFC5512"; + } + + identity TUNNEL_COLOR { + base TUNNEL_ENCAPSULATION_SUBTLV_TYPE; + description + "Colour of the tunnel"; + reference + "RFC5512"; + } + + identity SRTE_PREFERENCE { + base TUNNEL_ENCAPSULATION_SUBTLV_TYPE; + description + "Preference of the SR-TE policy entry described by + the tunnel encapsulation attribute."; + reference + "draft-previdi-idr-segment-routing-te-policy"; + } + + identity SRTE_BINDING_SID { + base TUNNEL_ENCAPSULATION_SUBTLV_TYPE; + description + "Binding SID to be used by the SR-TE policy described + by the tunnel encapsulation attribute."; + reference + "draft-previdi-idr-segment-routing-te-policy"; + } + + identity SRTE_SEGMENT_LIST { + base TUNNEL_ENCAPSULATION_SUBTLV_TYPE; + description + "Segment lists to be used by the SR-TE policy described + by the tunnel encapsulation attribute."; + reference + "draft-previdi-idr-segment-routing-te-policy"; + } + + identity SRTE_SEGMENT_LIST_SUBTLV { + description + "SubTLVs of the SR-TE Policy Segment List sub-TLV of the + Tunnel Encapsulation attribute."; + reference + "draft-previdi-idr-segment-routing-te-policy"; + } + + identity SRTE_SEGMENT_LIST_WEIGHT { + base SRTE_SEGMENT_LIST_SUBTLV; + description + "Weight of the segment list within the SR-TE policy"; + reference + "draft-previdi-idr-segment-routing-te-policy"; + } + + identity SRTE_SEGMENT_LIST_SEGMENT { + base SRTE_SEGMENT_LIST_SUBTLV; + description + "An individual element within the SR-TE Policy Segment + List."; + } +} diff --git a/models/yang/common/openconfig-rib-bgp.yang b/models/yang/common/openconfig-rib-bgp.yang new file mode 100644 index 0000000000..dead3b3e1e --- /dev/null +++ b/models/yang/common/openconfig-rib-bgp.yang @@ -0,0 +1,226 @@ +module openconfig-rib-bgp { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/rib/bgp"; + + prefix "oc-rib-bgp"; + + // import some basic types + import openconfig-bgp-types { prefix oc-bgpt; } + import openconfig-extensions { prefix oc-ext; } + + // include RIB submodules + + // structure for LOC-RIB and Adj-RIB tables + include openconfig-rib-bgp-tables; + + // structure of shared attribute groups + include openconfig-rib-bgp-shared-attributes; + + // groupings of attributes in three categories: + // - shared across multiple routes + // - common to LOC-RIB and Adj-RIB, but not shared across routes + // - specific to LOC-RIB or Adj-RIB + include openconfig-rib-bgp-attributes; + + // groupings of annotations for each route or table + include openconfig-rib-bgp-table-attributes; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "Defines a data model for representing BGP routing table (RIB) + contents. The model supports 5 logical RIBs per address family: + + loc-rib: This is the main BGP routing table for the local routing + instance, containing best-path selections for each prefix. The + loc-rib table may contain multiple routes for a given prefix, + with an attribute to indicate which was selected as the best + path. Note that multiple paths may be used or advertised even if + only one path is marked as best, e.g., when using BGP + add-paths. An implementation may choose to mark multiple + paths in the RIB as best path by setting the flag to true for + multiple entries. + + adj-rib-in-pre: This is a per-neighbor table containing the NLRI + updates received from the neighbor before any local input policy + rules or filters have been applied. This can be considered the + 'raw' updates from a given neighbor. + + adj-rib-in-post: This is a per-neighbor table containing the + routes received from the neighbor that are eligible for + best-path selection after local input policy rules have been + applied. + + adj-rib-out-pre: This is a per-neighbor table containing routes + eligible for sending (advertising) to the neighbor before output + policy rules have been applied. + + adj-rib-out-post: This is a per-neighbor table containing routes + eligible for sending (advertising) to the neighbor after output + policy rules have been applied."; + + oc-ext:openconfig-version "0.5.0"; + + revision "2019-04-16" { + description + "Rename the top-level BGP RIB container's name + to RIB."; + reference "0.5.0"; + } + + revision "2019-02-27" { + description + "Remove top-level BGP RIB container, and update list + names to be compatible with path compression."; + reference "0.4.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2016-10-17" { + description + "OpenConfig BGP RIB refactor"; + reference "0.3.0"; + } + + revision "2016-04-11" { + description + "OpenConfig public release"; + reference "0.2.0"; + } + + + + // grouping statements + + + + grouping bgp-rib-state { + description + "Operational state data for the top level BGP RIB"; + + leaf afi-safi-name { + type identityref { + base oc-bgpt:AFI_SAFI_TYPE; + } + description "AFI,SAFI"; + } + } + + grouping bgp-rib-top { + description + "Top-level grouping for the BGP RIB"; + + container rib { + config false; + description + "Top level container for BGP RIBs"; + + uses attribute-sets-top; + uses community-sets-top; + uses ext-community-sets-top; + + container afi-safis { + config false; + description + "Enclosing container for address family list"; + + list afi-safi { + key "afi-safi-name"; + description + "list of afi-safi types"; + + leaf afi-safi-name { + type leafref { + path "../state/afi-safi-name"; + } + description + "Reference to the list key"; + } + + container state { + config false; + description + "Operational state data for the BGP list"; + + uses bgp-rib-state; + } + + container ipv4-unicast { + when "../afi-safi-name = 'oc-bgpt:IPV4_UNICAST'" { + description + "Include this container for IPv4 unicast RIB"; + } + description + "Routing tables for IPv4 unicast -- active when the + afi-safi name is ipv4-unicast"; + + uses ipv4-loc-rib-top; + uses ipv4-adj-rib-top; + } + + container ipv6-unicast { + when "../afi-safi-name = 'oc-bgpt:IPV6_UNICAST'" { + description + "Include this container for IPv6 unicast RIB"; + } + description + "Routing tables for IPv6 unicast -- active when the + afi-safi name is ipv6-unicast"; + + uses ipv6-loc-rib-top; + uses ipv6-adj-rib-top; + } + + container ipv4-srte-policy { + when "../afi-safi-name = 'oc-bgpt:SRTE_POLICY_IPV4'" { + description + "Include this container only for the IPv4 AFI, SR-TE Policy + SAFI."; + } + description + "Routing tables for the IPv4 Unicast, SR-TE Policy SAFI."; + + uses ipvX-srte-policy-locrib-top; + uses ipvX-srte-policy-adjrib-top; + } + + container ipv6-srte-policy { + when "../afi-safi-name = 'oc-bgpt:SRTE_POLICY_IPV6'" { + description + "Include this container only for the IPv6 AFI, SR-TE Policy + SAFI."; + } + description + "Routing tables for the IPv6 Unicast, SR-TE Policy SAFI."; + + uses ipvX-srte-policy-locrib-top; + uses ipvX-srte-policy-adjrib-top; + } + } + } + } + } + + + // data definition statements + // augment statements + + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-routing-policy.yang b/models/yang/common/openconfig-routing-policy.yang new file mode 100644 index 0000000000..28643dc8b3 --- /dev/null +++ b/models/yang/common/openconfig-routing-policy.yang @@ -0,0 +1,1107 @@ +module openconfig-routing-policy { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/routing-policy"; + + prefix "oc-rpol"; + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-policy-types { prefix oc-pol-types; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module describes a YANG model for routing policy + configuration. It is a limited subset of all of the policy + configuration parameters available in the variety of vendor + implementations, but supports widely used constructs for managing + how routes are imported, exported, and modified across different + routing protocols. This module is intended to be used in + conjunction with routing protocol configuration models (e.g., + BGP) defined in other modules. + + Route policy expression: + + Policies are expressed as a set of top-level policy definitions, + each of which consists of a sequence of policy statements. Policy + statements consist of simple condition-action tuples. Conditions + may include mutiple match or comparison operations, and similarly + actions may be multitude of changes to route attributes or a + final disposition of accepting or rejecting the route. + + Route policy evaluation: + + Policy definitions are referenced in routing protocol + configurations using import and export configuration statements. + The arguments are members of an ordered list of named policy + definitions which comprise a policy chain, and optionally, an + explicit default policy action (i.e., reject or accept). + + Evaluation of each policy definition proceeds by evaluating its + corresponding individual policy statements in order. When a + condition statement in a policy statement is satisfied, the + corresponding action statement is executed. If the action + statement has either accept-route or reject-route actions, policy + evaluation of the current policy definition stops, and no further + policy definitions in the chain are evaluated. + + If the condition is not satisfied, then evaluation proceeds to + the next policy statement. If none of the policy statement + conditions are satisfied, then evaluation of the current policy + definition stops, and the next policy definition in the chain is + evaluated. When the end of the policy chain is reached, the + default route disposition action is performed (i.e., reject-route + unless an an alternate default action is specified for the + chain). + + Policy 'subroutines' (or nested policies) are supported by + allowing policy statement conditions to reference another policy + definition which applies conditions and actions from the + referenced policy before returning to the calling policy + statement and resuming evaluation. If the called policy + results in an accept-route (either explicit or by default), then + the subroutine returns an effective true value to the calling + policy. Similarly, a reject-route action returns false. If the + subroutine returns true, the calling policy continues to evaluate + the remaining conditions (using a modified route if the + subroutine performed any changes to the route)."; + + oc-ext:openconfig-version "3.1.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.1.1"; + } + + revision "2018-06-05" { + description + "Add PIM, IGMP to INSTALL_PROTOCOL_TYPES identity"; + reference "3.1.0"; + } + + revision "2017-07-14" { + description + "Replace policy choice node/type with policy-result + enumeration;simplified defined set naming;removed generic + IGP actions; migrate to OpenConfig types; added mode for + prefix sets"; + reference "3.0.0"; + } + + revision "2016-05-12" { + description + "OpenConfig public release"; + reference "2.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedef statements + + typedef default-policy-type { + // this typedef retained for name compatibiity with default + // import and export policy + type enumeration { + enum ACCEPT_ROUTE { + description + "Default policy to accept the route"; + } + enum REJECT_ROUTE { + description + "Default policy to reject the route"; + } + } + description + "Type used to specify route disposition in + a policy chain"; + } + + typedef policy-result-type { + type enumeration { + enum ACCEPT_ROUTE { + description "Policy accepts the route"; + } + enum REJECT_ROUTE { + description "Policy rejects the route"; + } + } + description + "Type used to specify route disposition in + a policy chain"; + } + + + // grouping statements + + grouping prefix-set-config { + description + "Configuration data for prefix sets used in policy + definitions."; + + leaf name { + type string; + description + "name / label of the prefix set -- this is used to + reference the set in match conditions"; + } + + leaf mode { + type enumeration { + enum IPV4 { + description + "Prefix set contains IPv4 prefixes only"; + } + enum IPV6 { + description + "Prefix set contains IPv6 prefixes only"; + } + enum MIXED { + description + "Prefix set contains mixed IPv4 and IPv6 prefixes"; + } + } + description + "Indicates the mode of the prefix set, in terms of which + address families (IPv4, IPv6, or both) are present. The + mode provides a hint, but the device must validate that all + prefixes are of the indicated type, and is expected to + reject the configuration if there is a discrepancy. The + MIXED mode may not be supported on devices that require + prefix sets to be of only one address family."; + } + + } + + grouping prefix-set-state { + description + "Operational state data for prefix sets"; + } + + grouping prefix-set-top { + description + "Top-level data definitions for a list of IPv4 or IPv6 + prefixes which are matched as part of a policy"; + + container prefix-sets { + description + "Enclosing container "; + + list prefix-set { + key "name"; + description + "List of the defined prefix sets"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to prefix name list key"; + } + + container config { + description + "Configuration data for prefix sets"; + + uses prefix-set-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses prefix-set-config; + uses prefix-set-state; + } + + uses prefix-top; + } + } + } + + grouping prefix-config { + description + "Configuration data for a prefix definition"; + + leaf ip-prefix { + type oc-inet:ip-prefix; + mandatory true; + description + "The prefix member in CIDR notation -- while the + prefix may be either IPv4 or IPv6, most + implementations require all members of the prefix set + to be the same address family. Mixing address types in + the same prefix set is likely to cause an error."; + } + + leaf masklength-range { + type string { + pattern '^([0-9]+\.\.[0-9]+)|exact$'; + } + description + "Defines a range for the masklength, or 'exact' if + the prefix has an exact length. + + Example: 10.3.192.0/21 through 10.3.192.0/24 would be + expressed as prefix: 10.3.192.0/21, + masklength-range: 21..24. + + Example: 10.3.192.0/21 would be expressed as + prefix: 10.3.192.0/21, + masklength-range: exact"; + } + } + + grouping prefix-state { + description + "Operational state data for prefix definitions"; + } + + grouping prefix-top { + description + "Top-level grouping for prefixes in a prefix list"; + + container prefixes { + description + "Enclosing container for the list of prefixes in a policy + prefix list"; + + list prefix { + key "ip-prefix masklength-range"; + description + "List of prefixes in the prefix set"; + + leaf ip-prefix { + type leafref { + path "../config/ip-prefix"; + } + description + "Reference to the ip-prefix list key."; + } + + leaf masklength-range { + type leafref { + path "../config/masklength-range"; + } + description + "Reference to the masklength-range list key"; + } + + container config { + description + "Configuration data for prefix definition"; + + uses prefix-config; + } + + container state { + + config false; + + description + "Operational state data for prefix definition"; + + uses prefix-config; + uses prefix-state; + } + } + } + } + + grouping neighbor-set-config { + description + "Configuration data for neighbor set definitions"; + + leaf name { + type string; + description + "name / label of the neighbor set -- this is used to + reference the set in match conditions"; + } + + leaf-list address { + type oc-inet:ip-address; + description + "List of IP addresses in the neighbor set"; + } + } + + grouping neighbor-set-state { + description + "Operational state data for neighbor set definitions"; + } + + grouping neighbor-set-top { + description + "Top-level data definition for a list of IPv4 or IPv6 + neighbors which can be matched in a routing policy"; + + container neighbor-sets { + description + "Enclosing container for the list of neighbor set + definitions"; + + list neighbor-set { + key "name"; + description + "List of defined neighbor sets for use in policies."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the neighbor set name list key."; + } + + container config { + description + "Configuration data for neighbor sets."; + + uses neighbor-set-config; + } + + container state { + + config false; + + description + "Operational state data for neighbor sets."; + + uses neighbor-set-config; + uses neighbor-set-state; + } + } + } + } + + grouping tag-set-config { + description + "Configuration data for tag set definitions."; + + leaf name { + type string; + description + "name / label of the tag set -- this is used to reference + the set in match conditions"; + } + + leaf-list tag-value { + type oc-pol-types:tag-type; + description + "Value of the tag set member"; + } + } + + grouping tag-set-state { + description + "Operational state data for tag set definitions."; + } + + grouping tag-set-top { + description + "Top-level data definitions for a list of tags which can + be matched in policies"; + + container tag-sets { + description + "Enclosing container for the list of tag sets."; + + list tag-set { + key "name"; + description + "List of tag set definitions."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the tag set name list key"; + } + + container config { + description + "Configuration data for tag sets"; + + uses tag-set-config; + } + + container state { + + config false; + + description + "Operational state data for tag sets"; + + uses tag-set-config; + uses tag-set-state; + } + } + } + } + + grouping generic-defined-sets { + description + "Data definitions for pre-defined sets of attributes used in + policy match conditions. These sets are generic and can + be used in matching conditions in different routing + protocols."; + + uses prefix-set-top; + uses neighbor-set-top; + uses tag-set-top; + } + + grouping match-set-options-group { + description + "Grouping containing options relating to how a particular set + should be matched"; + + leaf match-set-options { + type oc-pol-types:match-set-options-type; + description + "Optional parameter that governs the behaviour of the + match operation"; + } + } + + grouping match-set-options-restricted-group { + description + "Grouping for a restricted set of match operation modifiers"; + + leaf match-set-options { + type oc-pol-types:match-set-options-restricted-type; + description + "Optional parameter that governs the behaviour of the + match operation. This leaf only supports matching on ANY + member of the set or inverting the match. Matching on ALL is + not supported"; + } + } + + grouping match-interface-condition-config { + description + "Configuration data for interface match condition"; + + uses oc-if:interface-ref-common; + } + + grouping match-interface-condition-state { + description + "Operational state data for interface match condition"; + } + + grouping match-interface-condition-top { + description + "Top-level grouping for the interface match condition"; + + container match-interface { + description + "Top-level container for interface match conditions"; + + container config { + description + "Configuration data for interface match conditions"; + + uses match-interface-condition-config; + } + + container state { + + config false; + + description + "Operational state data for interface match conditions"; + + uses match-interface-condition-config; + uses match-interface-condition-state; + } + + } + } + + grouping prefix-set-condition-config { + description + "Configuration data for prefix-set conditions"; + + leaf prefix-set { + type leafref { + path "../../../../../../../../defined-sets/" + + "prefix-sets/prefix-set/config/name"; + } + description "References a defined prefix set"; + } + uses match-set-options-restricted-group; + } + + + grouping prefix-set-condition-state { + description + "Operational state data for prefix-set conditions"; + } + + grouping prefix-set-condition-top { + description + "Top-level grouping for prefix-set conditions"; + + container match-prefix-set { + description + "Match a referenced prefix-set according to the logic + defined in the match-set-options leaf"; + + container config { + description + "Configuration data for a prefix-set condition"; + + uses prefix-set-condition-config; + } + + container state { + + config false; + + description + "Operational state data for a prefix-set condition"; + + uses prefix-set-condition-config; + uses prefix-set-condition-state; + } + } + } + + grouping neighbor-set-condition-config { + description + "Configuration data for neighbor-set conditions"; + + leaf neighbor-set { + type leafref { + path "../../../../../../../../defined-sets/neighbor-sets/" + + "neighbor-set/name"; + //TODO: require-instance should be added when it's + //supported in YANG 1.1 + //require-instance true; + } + description "References a defined neighbor set"; + } + + uses match-set-options-restricted-group; + } + + grouping neighbor-set-condition-state { + description + "Operational state data for neighbor-set conditions"; + } + + grouping neighbor-set-condition-top { + description + "Top-level grouping for neighbor-set conditions"; + + container match-neighbor-set { + description + "Match a referenced neighbor set according to the logic + defined in the match-set-options-leaf"; + + container config { + description + "Configuration data "; + + uses neighbor-set-condition-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses neighbor-set-condition-config; + uses neighbor-set-condition-state; + } + } + } + + grouping tag-set-condition-config { + description + "Configuration data for tag-set condition statements"; + + leaf tag-set { + type leafref { + path "../../../../../../../../defined-sets/tag-sets/tag-set" + + "/name"; + //TODO: require-instance should be added when it's + //supported in YANG 1.1 + //require-instance true; + } + description "References a defined tag set"; + } + uses match-set-options-restricted-group; + } + + grouping tag-set-condition-state { + description + "Operational state data for tag-set condition statements"; + } + + grouping tag-set-condition-top { + description + "Top-level grouping for tag-set conditions"; + + container match-tag-set { + description + "Match a referenced tag set according to the logic defined + in the match-options-set leaf"; + + container config { + description + "Configuration data for tag-set conditions"; + + uses tag-set-condition-config; + } + + container state { + + config false; + + description + "Operational state data tag-set conditions"; + + uses tag-set-condition-config; + uses tag-set-condition-state; + } + } + } + + grouping generic-conditions { + description "Condition statement definitions for checking + membership in a generic defined set"; + + uses match-interface-condition-top; + uses prefix-set-condition-top; + uses neighbor-set-condition-top; + uses tag-set-condition-top; + + } + + grouping generic-actions { + description + "Definitions for common set of policy action statements that + manage the disposition or control flow of the policy"; + + leaf policy-result { + type policy-result-type; + description + "Select the final disposition for the route, either + accept or reject."; + } + } + + + grouping policy-conditions-config { + description + "Configuration data for general policy conditions, i.e., those + not related to match-sets"; + + leaf call-policy { + type leafref { + path "../../../../../../../" + + "oc-rpol:policy-definitions/" + + "oc-rpol:policy-definition/oc-rpol:name"; + //TODO: require-instance should be added when + //it is supported in YANG 1.1 + //require-instance true; + } + description + "Applies the statements from the specified policy + definition and then returns control the current + policy statement. Note that the called policy may + itself call other policies (subject to + implementation limitations). This is intended to + provide a policy 'subroutine' capability. The + called policy should contain an explicit or a + default route disposition that returns an + effective true (accept-route) or false + (reject-route), otherwise the behavior may be + ambiguous and implementation dependent"; + } + + leaf install-protocol-eq { + type identityref { + base oc-pol-types:INSTALL_PROTOCOL_TYPE; + } + description + "Condition to check the protocol / method used to install + the route into the local routing table"; + } + } + + grouping policy-conditions-state { + description + "Operational state data for policy conditions"; + } + + grouping policy-conditions-top { + description + "Top-level grouping for policy conditions"; + + container conditions { + description + "Condition statements for the current policy statement"; + + container config { + description + "Configuration data for policy conditions"; + + uses policy-conditions-config; + } + + container state { + + config false; + + description + "Operational state data for policy conditions"; + + uses policy-conditions-config; + uses policy-conditions-state; + } + uses generic-conditions; + } + } + + grouping policy-statements-config { + description + "Configuration data for policy statements"; + + leaf name { + type string; + description + "name of the policy statement"; + } + } + + grouping policy-statements-state { + description + "Operational state data for policy statements"; + } + + + + grouping policy-actions-config { + description + "Configuration data for policy actions"; + + uses generic-actions; + } + + grouping policy-actions-state { + description + "Operational state data for policy actions"; + } + + grouping policy-actions-top { + description + "Top-level grouping for policy actions"; + + container actions { + description + "Top-level container for policy action statements"; + + container config { + description + "Configuration data for policy actions"; + + uses policy-actions-config; + } + + container state { + + config false; + + description + "Operational state data for policy actions"; + + uses policy-actions-config; + uses policy-actions-state; + } + } + } + + grouping policy-statements-top { + description + "Top-level grouping for the policy statements list"; + + container statements { + description + "Enclosing container for policy statements"; + + list statement { + key "name"; + // TODO: names of policy statements within a policy + // definition should probably be optional, however, YANG + // requires a unique id for lists + ordered-by user; + description + "Policy statements group conditions and actions + within a policy definition. They are evaluated in + the order specified (see the description of policy + evaluation at the top of this module."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to list key"; + } + + container config { + description + "Configuration data for policy statements"; + + uses policy-statements-config; + } + + container state { + + config false; + + description + "Operational state data for policy statements"; + + uses policy-statements-config; + uses policy-statements-state; + } + + uses policy-conditions-top; + uses policy-actions-top; + } + } + } + + grouping defined-sets-top { + description + "Top-level grouping for defined set definitions"; + + container defined-sets { + description + "Predefined sets of attributes used in policy match + statements"; + + uses generic-defined-sets; + } + } + + grouping policy-definitions-config { + description + "Configuration data for policy definitions"; + + leaf name { + type string; + description + "Name of the top-level policy definition -- this name + is used in references to the current policy"; + } + } + + grouping policy-definitions-state { + description + "Operational state data for policy definitions"; + } + + grouping policy-definitions-top { + description + "Top-level grouping for the policy definition list"; + + container policy-definitions { + description + "Enclosing container for the list of top-level policy + definitions"; + + list policy-definition { + key "name"; + description + "List of top-level policy definitions, keyed by unique + name. These policy definitions are expected to be + referenced (by name) in policy chains specified in import + or export configuration statements."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for policy defintions"; + + uses policy-definitions-config; + } + + container state { + + config false; + + description + "Operational state data for policy definitions"; + + uses policy-definitions-config; + uses policy-definitions-state; + } + + uses policy-statements-top; + } + } + } + + grouping routing-policy-top { + description + "Top level container for OpenConfig routing policy"; + + container routing-policy { + description + "Top-level container for all routing policy configuration"; + + + uses defined-sets-top; + + uses policy-definitions-top; + } + } + + grouping apply-policy-import-config { + description + "Configuration data for applying import policies"; + + leaf-list import-policy { + type leafref { + path "/oc-rpol:routing-policy/oc-rpol:policy-definitions/" + + "oc-rpol:policy-definition/oc-rpol:name"; + //TODO: require-instance should be added when it's + //supported in YANG 1.1 + //require-instance true; + } + ordered-by user; + description + "list of policy names in sequence to be applied on + receiving a routing update in the current context, e.g., + for the current peer group, neighbor, address family, + etc."; + } + + leaf default-import-policy { + type default-policy-type; + default REJECT_ROUTE; + description + "explicitly set a default policy if no policy definition + in the import policy chain is satisfied."; + } + + } + + grouping apply-policy-export-config { + description + "Configuration data for applying export policies"; + + leaf-list export-policy { + type leafref { + path "/oc-rpol:routing-policy/oc-rpol:policy-definitions/" + + "oc-rpol:policy-definition/oc-rpol:name"; + //TODO: require-instance should be added when it's + //supported in YANG 1.1 + //require-instance true; + } + ordered-by user; + description + "list of policy names in sequence to be applied on + sending a routing update in the current context, e.g., + for the current peer group, neighbor, address family, + etc."; + } + + leaf default-export-policy { + type default-policy-type; + default REJECT_ROUTE; + description + "explicitly set a default policy if no policy definition + in the export policy chain is satisfied."; + } + } + + + grouping apply-policy-config { + description + "Configuration data for routing policies"; + + uses apply-policy-import-config; + uses apply-policy-export-config; + + } + + + + grouping apply-policy-state { + description + "Operational state associated with routing policy"; + + //TODO: identify additional state data beyond the intended + //policy configuration. + } + + grouping apply-policy-group { + description + "Top level container for routing policy applications. This + grouping is intended to be used in routing models where + needed."; + + container apply-policy { + description + "Anchor point for routing policies in the model. + Import and export policies are with respect to the local + routing table, i.e., export (send) and import (receive), + depending on the context."; + + container config { + description + "Policy configuration data."; + + uses apply-policy-config; + } + + container state { + + config false; + description + "Operational state for routing policy"; + + uses apply-policy-config; + uses apply-policy-state; + } + } + } + + uses routing-policy-top; + +} diff --git a/models/yang/common/openconfig-segment-routing.yang b/models/yang/common/openconfig-segment-routing.yang new file mode 100644 index 0000000000..0dbaf64cbe --- /dev/null +++ b/models/yang/common/openconfig-segment-routing.yang @@ -0,0 +1,791 @@ +module openconfig-segment-routing { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/sr"; + prefix "oc-sr"; + + // import some basic types + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-mpls-types { prefix "oc-mpls-t"; } + import openconfig-interfaces { prefix "oc-if"; } + import ietf-inet-types { prefix "inet"; } + import ietf-yang-types { prefix "yang"; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Configuration and operational state parameters relating to the + segment routing. This module defines a number of elements which are + instantiated in multiple places throughout the OpenConfig collection + of models. + + Particularly: + - SRGB+LB dataplane instances - directly instantied by SR. + - SRGB+LB dataplane reservations - instantiated within MPLS and future SR + dataplanes. + - SR SID advertisements - instantiated within the relevant IGP. + - SR-specific counters - instantied within the relevant dataplane."; + + oc-ext:openconfig-version "0.0.4"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.0.4"; + } + + revision "2017-01-12" { + description + "Minor compliance fixes."; + reference "0.0.3"; + } + + revision "2016-12-15" { + description + "Updated revision of SR module."; + reference "0.0.2"; + } + + revision "2016-07-28" { + description + "Initial revision of SR module."; + reference "0.0.1"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + typedef sr-dataplane-type { + type enumeration { + enum MPLS { + description + "The entity uses MPLS labels as Segment Identifiers."; + } + enum IPV6 { + description + "The entity uses IPv6 prefixes as Segment Identifiers."; + } + } + description + "Types of data plane that can be used to instantiate a Segment + Routing block of SIDs."; + } + + typedef sr-sid-type { + type union { + type oc-mpls-t:mpls-label; + type inet:ipv6-address-no-zone; + } + description + "The defined value of a segment identifier."; + } + + grouping srgb-config { + description + "Configuration parameters relating to the SRGB."; + + leaf local-id { + type string; + description + "Unique identifier for the segment routing global block on + the local system."; + } + + leaf dataplane-type { + type sr-dataplane-type; + description + "The dataplane being used to instantiate the SRGB. When MPLS is specified + the set of MPLS label blocks that are defined in the mpls-label-blocks + list are used to make up the SRGB. When IPv6 is specified, the set of IPv6 + prefixes specified in the ipv6-prefixes list are used."; + } + + leaf-list mpls-label-blocks { + when "../dataplane-type = 'MPLS'" { + description + "Allow the MPLS label block to be specified only for SRGBs that are + using the MPLS dataplane."; + } + type leafref { + // We are at /network-instances/network-instance/segment-routing/ + // srgbs/srgb/config/mpls-label-blocks + path "../../../../../mpls/global/reserved-label-blocks/" + + "reserved-label-block/config/local-id"; + } + description + "A list of refences to the label blocks that are used to make + up the SRGB."; + } + + leaf-list ipv6-prefixes { + when "../dataplane-type = 'IPV6'" { + description + "Allow IPv6 prefixes to be specified only when the dataplane + realisation of the SRGB is IPv6."; + } + type inet:ipv6-prefix; + description + "A list of IPv6 prefixes which are to be used for segment routing using + the IPv6 dataplane."; + } + } + + grouping srgb-state { + description + "Operational state parameters relating to the SRGB."; + + leaf size { + type uint32; + description + "The total number of SRGB entries that are available within the SRGB."; + } + + leaf used { + type uint32; + description + "The total number of SRGB entries that have already been alocated by + protocols referencing the SRGB."; + } + } + + grouping srlb-config { + description + "Configuration parameters relating to an SRLB."; + + leaf local-id { + type string; + description + "A unique local identifier used for the Segment Routing Local Block. + The identifier is used when referencing the SRLB within other + contexts."; + } + + leaf dataplane-type { + type sr-dataplane-type; + description + "The dataplane that is to be used for the Segment Routing Local Block. + When MPLS is specified, the local block corresponds to a block of MPLS + labels; when IPv6 is specified it corresponds to an IPv6 prefix."; + } + + leaf mpls-label-block { + when "../dataplane-type = 'MPLS'" { + description + "Allow the MPLS label block to be specified only for SRLBs that are + using the MPLS dataplane."; + } + type leafref { + path "../../../../../mpls/global/reserved-label-blocks/" + + "reserved-label-block/config/local-id"; + } + description + "A reference to the MPLS label block that is used to contain the + SIDs of the SRLB."; + } + + leaf ipv6-prefix { + when "../dataplane-type = 'IPV6'" { + description + "Allow IPv6 prefixes to be specified only when the dataplane + realisation of the SRGB is IPv6."; + } + type inet:ipv6-prefix; + description + "The IPv6 prefix that is used for the SRLB."; + } + } + + grouping sr-structural { + description + "Top-level structural grouping defining Segment Routing Global Blocks."; + + container srgbs { + description + "Configuration and operational state parameters relating to the + SRGBs defined for the system."; + + list srgb { + key "local-id"; + + description + "A single definition of an SRGB which may comprise of multiple + sets of dataplane addresses (IPv6 addresses, or MPLS labels)."; + + leaf local-id { + type leafref { + path "../config/local-id"; + } + description + "A reference to the identifier for the SRGB."; + } + + container config { + description + "Configuration parameters relating to the SRGB."; + uses srgb-config; + } + + container state { + config false; + description + "State parameters relating to the SRGB."; + uses srgb-config; + uses srgb-state; + } + } + } + + container srlbs { + description + "Configuration and operational state parameters relating to the + Segment Routing Local Blocks (SRLBs) defined for the system."; + + list srlb { + key "local-id"; + + description + "A definition of a Segment Routing Local Block, defined to be + a set of Segment Identifiers (specified as MPLS labels or + IPv6 addreses) that are defined for local allocation by the + system. A block may optionally be advertised into an IGP."; + + leaf local-id { + type leafref { + path "../config/local-id"; + } + description + "Reference to the local identifier used for the SRLB."; + } + + container config { + description + "Configuration parameters relating to the SRLB."; + uses srlb-config; + } + + container state { + config false; + description + "Operational state parmeters relating to the SRLB."; + uses srlb-config; + } + } + } + } + + grouping sr-mpls-top { + description + "Structural grouping defining SR definition within MPLS."; + + container segment-routing { + description + "MPLS-specific Segment Routing configuration and operational state + parameters"; + + container aggregate-sid-counters { + description + "Per-SID counters aggregated across all interfaces on the local system"; + + list aggregate-sid-counter { + key "mpls-label"; + config false; + + description + "Counters aggregated across all of the interfaces of the local + system corresponding to traffic received or forwarded with a + particular SID"; + + leaf mpls-label { + type leafref { + path "../state/mpls-label"; + } + description + "The MPLS label representing the segment identifier"; + } + + container state { + config false; + description + "State parameters for per-SID statistics"; + uses sr-mpls-sid-counters-state; + uses sr-mpls-common-counters; + } + } + } + + container interfaces { + description + "Interface related Segment Routing parameters."; + + list interface { + key "interface-id"; + + description + "Parameters and MPLS-specific configuration relating to Segment + Routing on an interface."; + + leaf interface-id { + type leafref { + path "../config/interface-id"; + } + description + "A reference to the ID for the interface for which SR is + configured"; + } + + container config { + description + "MPLS-specific Segment Routing configuration parameters + related to an interface."; + uses sr-mpls-interface-config; + } + + container state { + config false; + description + "MPLS-specific Segment Routing operational state parameters + related to an interface."; + uses sr-mpls-interface-config; + uses sr-mpls-interface-state; + } + + container sid-counters { + description + "Per-SID statistics for MPLS"; + + list sid-counter { + key "mpls-label"; + config false; + + description + "Per segment identifier counters for the MPLS dataplane."; + + leaf mpls-label { + type leafref { + path "../state/mpls-label"; + } + description + "The MPLS label representing the segment identifier"; + } + + container state { + config false; + description + "State parameters for per-SID statistics"; + uses sr-mpls-sid-counters-state; + uses sr-mpls-common-counters; + } + + container forwarding-classes { + description + "Per-SID per-forwarding class counters for Segment Routing."; + + list forwarding-class { + key "exp"; + config false; + + description + "SID entries for the forwarding class associated with the + referenced MPLS EXP."; + + leaf exp { + type leafref { + path "../state/exp"; + } + description + "Reference to the value of the EXP bits of the segment + identifier."; + } + + container state { + config false; + description + "Per-SID, per forwarding class counters for Segment Routing + with the MPLS dataplane"; + + uses sr-mpls-interface-sid-fc-state; + uses sr-mpls-common-counters; + } + } + } + } + } + uses oc-if:interface-ref; + } + } + } + } + + grouping sr-mpls-interface-config { + description + "MPLS-specific Segment Routing configuration for an interface"; + + leaf interface-id { + type string; + description + "A unique identifier for the interface."; + } + } + + grouping sr-mpls-interface-state { + description + "MPLS-specific Segment Routing operational state parameters for an + interface"; + + uses sr-mpls-common-counters; + } + + grouping sr-mpls-interface-sid-fc-state { + description + "Per-SID per forwarding class statistics for SR with the MPLS dataplane"; + + leaf exp { + type uint8 { + range "0..7"; + } + description + "The value of the MPLS EXP (experimental) or Traffic Class bits that the + SID statistics relate to. Packets received with a MPLS label value + equal to the SID's MPLS label and EXP bits equal to the this value + should be counted towards the associated ingress statistics. Packets + that are forwarded to the destination MPLS label corresponding to the + SID should be counted towards this value. In the egress direction, where + forwarding follows a SID value that requires PHP at the local node, + packets should still be counted towards the egress counters."; + } + } + + grouping sr-mpls-sid-counters-state { + description + "Per-SID statistics leaves"; + + leaf mpls-label { + type oc-mpls-t:mpls-label; + description + "The MPLS label used for the segment identifier"; + } + } + + grouping sr-mpls-common-counters { + description + "Per segment identifier counters used in the model"; + + leaf in-pkts { + type yang:counter64; + description + "A cumulative counter of the packets received within the context + which have matched a label corresponding to an SR Segment Identifier."; + } + + leaf in-octets { + type yang:counter64; + description + "The cumulative counter of the total bytes received within the context + which have matched a label corresponding to an SR Segment Identifier"; + } + + leaf out-pkts { + type yang:counter64; + description + "A cumulative counter of the total number of packets transmitted by + the local system within the context which have a label imposed that + corresponds to an Segment Identifier."; + } + + leaf out-octets { + type yang:counter64; + description + "A cumulative counter of the total bytes transmitted by the local + system within the context which have a label imported that + corresponds to an SR Segment Identifier."; + } + } + + grouping sr-igp-config { + description + "Configuration parameters relating to segment routing within an + IGP."; + + leaf enabled { + type boolean; + description + "When this leaf is set to true, the segment routing extensions are + utilised within the IGP."; + } + + leaf srgb { + type leafref { + path "../../../../../../../segment-routing/srgbs/srgb/config/local-id"; + } + description + "A reference to the Segment Routing Global Block (SRGB) that is + to be used by this IGP instance."; + } + + leaf srlb { + // Leaf is defined at + // /network-instances/network-instance/protocols/protocol//global/ + // segment-routing/config + type leafref { + path "../../../../../../../segment-routing/srlbs/srlb/config/local-id"; + } + description + "A reference to the Segment Routing Local Block (SRLB) that is to + be advertised by the IGP instance."; + } + } + + grouping sr-igp-top { + description + "Per-instance configuration and state parameters for Segment Routing + in an IGP."; + + container segment-routing { + description + "Configuration and operational state relating to segment routing."; + + container config { + description + "Configuration parameters relating to the configuration of segment + routing for the IGP instance."; + uses sr-igp-config; + } + + container state { + config false; + description + "Operational state parameters relating to segment routing for the + IGP instance."; + uses sr-igp-config; + } + } + } + + grouping sr-igp-interface-prefix-sid-config { + description + "Configuration parameters relating to an IGP prefix SID advertisement"; + + leaf prefix { + type inet:ip-prefix; + description + "The IP prefix for which the IGP prefix SID should be advertised. The + value specified is a local prefix on the interface which is advertised + into the IGP."; + } + + leaf sid-id { + type sr-sid-type; + description + "The Segment Identifier to be used when advertising the IGP Prefix SID."; + } + + leaf label-options { + type enumeration { + enum NO_PHP { + description + "When this value is specified, the penultimate hop must not pop + the Prefix-SID label before forwarding it to the local system."; + } + enum EXPLICIT_NULL { + description + "When set, the penultimate hop must swap the prefix SID for the + relevant explicit null label before forwarding the packet."; + } + } + description + "The options associated with the IGP prefix SID for MPLS. The value + of this leaf specifies the option that the SID should be advertised + into the IGP with."; + } + } + + grouping sr-igp-interface-adjsid-config { + description + "Configuration parameters relating to an Adj-SID on an interface"; + + leaf sid-id { + type union { + type sr-sid-type; + type enumeration { + enum DYNAMIC { + description + "The SID chosen for the Adjacency SID should be dynamically + allocated from the system's dynamic range of Segment + Identifiers. For MPLS, this range should be considered to be + those labels that do not fall within a reserved label block."; + } + } + } + description + "The value of the Adj-SID to be advertised. Where a static SID + identifier is specified, this should be advertised directly by the + system. Where the DYNAMIC value is specified, this should be treated + as a dynamically allocated value. When the MPLS data plane is in use + the dynamic value should not fall within a reserved-label-block."; + } + + leaf protection-eligible { + type boolean; + default true; + description + "Whether the Adj-SID should be considered to be eligible for protection + using IP or MPLS FRR during a network failure. When this value is set to + true, the B-flag of the Adj-SID is set to 1, and the local system should + provide FRR paths for the associated label forwarding entry. When it is + set to false, the local system must not provide FRR for the specified + LFIB entry."; + } + + leaf group { + type boolean; + default false; + description + "When set to true, the Adj-SID is indicated to be part of a group, and + the G flag is set to 1 in the corresponding advertisement in the IGP."; + } + + leaf neighbor { + type inet:ip-address; + description + "The remote system on the interface with which the Adj-SID is + associated."; + } + } + + grouping sr-igp-interface-adjsid-state { + description + "Operational state parameters relating to the adjacency SID for an + interface"; + + leaf allocated-dynamic-local { + type sr-sid-type; + description + "Where an Adjacency SID with a dynamic value is to be allocated by + the system, this leaf reports to the value of the Adj-SID allocated + to this interface."; + } + } + + grouping sr-igp-interface-top { + description + "Per-interface configuration and operational state relating to an + interface within the IGP."; + + container segment-routing { + description + "Configuration and operatioanl state parameters relating to segment + routing for an interface within the IGP."; + + container prefix-sids { + description + "Configuration and operational state parameters relating to + the advertisement of a segment routing IGP-Prefix SID for this + interface."; + + list prefix-sid { + key "prefix"; + + description + "An IGP prefix that should have a segment routing IGP-Prefix SID + allocated to it. The value of the SID is specified by the SID ID, + as an absolute value. If the absolute value falls within the SRGB, + the Global flag should be advertised by the system."; + + leaf prefix { + type leafref { + path "../config/prefix"; + } + description + "Reference to the prefix for which the IGP-Prefix SID is to be + advertised"; + } + + container config { + description + "Configuration parameters for the IGP Prefix SID."; + uses sr-igp-interface-prefix-sid-config; + } + + container state { + config false; + description + "Operational state parameters for the IGP-Prefix SID."; + uses sr-igp-interface-prefix-sid-config; + } + } + } + + container adjacency-sids { + description + "Configuration and operational state parameters relating to + the advertisement of a segment routing adjacency SID for this + interface."; + + list adjacency-sid { + key "neighbor sid-id"; + + description + "An Adjacency SID to be advertised for the specified interface. + The Adj-SID's identifier (the SID ID) must be unique, with flags + specified indicating the parameters that should be set for the SID. + Where a SID value is specified that is allocated from the SRGB, the + global flag must be set by the system."; + + leaf sid-id { + type leafref { + path "../config/sid-id"; + } + description + "Reference to the segment identifier to be used by the local + system."; + } + + leaf neighbor { + type leafref { + path "../config/neighbor"; + } + description + "Reference to the neighbor with which the Adjacency SID is + associated."; + } + + container config { + description + "Configuraton parameters relating to the AdjSID."; + uses sr-igp-interface-adjsid-config; + } + + container state { + config false; + description + "Operational state parameters relating to the AdjSID."; + uses sr-igp-interface-adjsid-config; + uses sr-igp-interface-adjsid-state; + } + } + } + } + } + + grouping sr-top { + description + "Top level grouping for Segment Routing"; + + container segment-routing { + description + "Configuration and operational state parameters relating to + segment routing."; + + uses sr-structural; + } + } +} diff --git a/models/yang/common/openconfig-spanning-tree-types.yang b/models/yang/common/openconfig-spanning-tree-types.yang new file mode 100755 index 0000000000..3a1c0bcbe4 --- /dev/null +++ b/models/yang/common/openconfig-spanning-tree-types.yang @@ -0,0 +1,243 @@ +module openconfig-spanning-tree-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/spanning-tree/types"; + + prefix "oc-stp-types"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines types related to the + spanning-tree protocol model."; + + oc-ext:openconfig-version "0.3.0"; + + revision "2014-03-14" { + description + "Remove the time-since-last-topology-change leaf and + replace it with a timestamp of last topology change."; + reference "0.3.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2017-07-14" { + description + "Migrated to OpenConfig types; fixed missing applied state + in rapid-pvst"; + reference "0.2.0"; + } + + revision "2016-10-03" { + description + "Initial public revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity STP_PROTOCOL { + description + "base identity for support spanning tree protocol"; + } + + identity RSTP { + base STP_PROTOCOL; + description + "Rapid Spanning Tree Protocol"; + reference "IEEE 802.1D 17"; + } + + identity MSTP { + base STP_PROTOCOL; + description + "Multiple Spanning Tree Protocol"; + reference "IEEE 802.1Q 13"; + } + + identity RAPID_PVST { + base STP_PROTOCOL; + description + "Rapid Per Vlan Spanning Tree Protocol"; + } + + identity STP_PORT_STATE { + description + "base identity for the different Spanning Tree Protocol port + states"; + reference + "IEEE 802.1D 7.4 Port States and the active topology"; + } + + identity DISABLED { + base STP_PORT_STATE; + description + "A port that is manually isolated from the network"; + } + + identity LISTENING { + base STP_PORT_STATE; + description + "Processing BPDUs and building active toplogy"; + } + + identity LEARNING { + base STP_PORT_STATE; + description + "Building bridging tables; no forwarding of data"; + } + + identity BLOCKING { + base STP_PORT_STATE; + description + "A port that would cause a loop if it were sending data, + so it is only receiving BPDUs, untill a topology change + removes the possibliity of a loop"; + } + + identity FORWARDING { + base STP_PORT_STATE; + description + "Sending and receiving data, normal operation"; + } + + identity STP_EDGE_PORT { + description + "base identity for the different edge port modes"; + reference + "IEEE 802.1D 17.13.1"; + } + + identity EDGE_ENABLE { + base STP_EDGE_PORT; + description + "Enable edge port for the bridge port"; + } + + identity EDGE_DISABLE { + base STP_EDGE_PORT; + description + "Disable edge port for the bridge port"; + } + + identity EDGE_AUTO { + base STP_EDGE_PORT; + description + "Enable edge port autodetction for the bridge port"; + } + + identity STP_PORT_ROLE { + description + "Base identity for the different Spanning Tree Protocol port + roles"; + reference + "IEEE 802.1D 17.7 Port Role assignments"; + } + + identity ROOT { + base STP_PORT_ROLE; + description + "The port that receives the best BPDU on a bridge is the + root port"; + } + + identity DESIGNATED { + base STP_PORT_ROLE; + description + "A port is designated if it can send the best BPDU on the + segment to which it is connected."; + } + + identity ALTERNATE { + base STP_PORT_ROLE; + description + "An alternate port receives more useful BPDUs from another + bridge and is a port blocked"; + } + + identity BACKUP { + base STP_PORT_ROLE; + description + "A backup port receives more useful BPDUs from the same + bridge it is on and is a port blocked"; + } + + // typedef statements + + typedef stp-bridge-priority-type { + type uint32 { + range 1..611440; + } + description + "The manageable component of the Bridge Identifier"; + reference "IEEE 802.1D 17.13.7 Bridge Identifier Priority"; + } + + typedef stp-port-priority-type { + type uint8 { + range 1..240; + } + description + "The manageable component of the Port Identifier, + also known as the Port Priority"; + reference + "IEEE 802.1D 17.13.10 Port Identifier Priority"; + } + + typedef stp-guard-type { + type enumeration { + enum ROOT { + description + "Enable root guard"; + } + enum LOOP { + description + "Enable loop guard"; + } + enum NONE { + description + "disable guard"; + } + } + description + "Type definition for the different STP guard for the switch port"; + reference "IEEE 802.1D 17.2"; + } + + typedef stp-link-type { + type enumeration { + enum P2P { + description + "Point-to-Point link"; + } + enum SHARED { + description + "Shared link"; + } + } + description + "Type definition for the different link types"; + reference "IEEE 802.1D 17.2"; + } +} diff --git a/models/yang/common/openconfig-system-logging.yang b/models/yang/common/openconfig-system-logging.yang new file mode 100644 index 0000000000..1602cb1c66 --- /dev/null +++ b/models/yang/common/openconfig-system-logging.yang @@ -0,0 +1,503 @@ +module openconfig-system-logging { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/system/logging"; + + prefix "oc-log"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + import openconfig-inet-types { prefix oc-inet; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + for common logging facilities on network systems."; + + oc-ext:openconfig-version "0.3.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // extension statements + + // feature statements + + // identity statements + + identity SYSLOG_FACILITY { + description + "Base identity for Syslog message facilities."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity ALL { + base SYSLOG_FACILITY; + description + "All supported facilities"; + } + + identity KERNEL { + base SYSLOG_FACILITY; + description + "The facility for kernel messages"; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity USER { + base SYSLOG_FACILITY; + description + "The facility for user-level messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity MAIL { + base SYSLOG_FACILITY; + description + "The facility for the mail system."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity SYSTEM_DAEMON { + base SYSLOG_FACILITY; + description + "The facility for the system daemons."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity AUTH { + base SYSLOG_FACILITY; + description + "The facility for security/authorization messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity SYSLOG { + base SYSLOG_FACILITY; + description + "The facility for messages generated internally by syslogd + facility."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity AUTHPRIV { + base SYSLOG_FACILITY; + description + "The facility for privileged security/authorization messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + + identity NTP { + base SYSLOG_FACILITY; + description + "The facility for the NTP subsystem."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity AUDIT { + base SYSLOG_FACILITY; + description + "The facility for log audit messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity CONSOLE { + base SYSLOG_FACILITY; + description + "The facility for log alert messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL0 { + base SYSLOG_FACILITY; + description + "The facility for local use 0 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL1 { + base SYSLOG_FACILITY; + description + "The facility for local use 1 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL2 { + base SYSLOG_FACILITY; + description + "The facility for local use 2 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL3 { + base SYSLOG_FACILITY; + description + "The facility for local use 3 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL4 { + base SYSLOG_FACILITY; + description + "The facility for local use 4 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL5 { + base SYSLOG_FACILITY; + description + "The facility for local use 5 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL6 { + base SYSLOG_FACILITY; + description + "The facility for local use 6 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOCAL7 { + base SYSLOG_FACILITY; + description + "The facility for local use 7 messages."; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + identity LOG_DESTINATION_TYPE { + description + "Base identity for destination for logging messages"; + } + + identity DEST_CONSOLE { + base LOG_DESTINATION_TYPE; + description + "Directs log messages to the console"; + } + + identity DEST_BUFFER { + base LOG_DESTINATION_TYPE; + description + "Directs log messages to and in-memory circular buffer"; + } + + identity DEST_FILE { + base LOG_DESTINATION_TYPE; + description + "Directs log messages to a local file"; + } + + identity DEST_REMOTE { + base LOG_DESTINATION_TYPE; + description + "Directs log messages to a remote syslog server"; + } + + // typedef statements + + typedef syslog-severity { + type enumeration { + enum EMERGENCY { + description + "Emergency: system is unusable (0)"; + } + enum ALERT { + description + "Alert: action must be taken immediately (1)"; + } + enum CRITICAL { + description + "Critical: critical conditions (2)"; + } + enum ERROR { + description + "Error: error conditions (3)"; + } + enum WARNING { + description + "Warning: warning conditions (4)"; + } + enum NOTICE { + description + "Notice: normal but significant condition(5)"; + } + enum INFORMATIONAL { + description + "Informational: informational messages (6)"; + } + enum DEBUG { + description + "Debug: debug-level messages (7)"; + } + } + description + "Syslog message severities"; + reference + "IETF RFC 5424 - The Syslog Protocol"; + } + + // grouping statements + + grouping logging-selectors-config { + description + "Configuration data for logging selectors"; + + leaf facility { + type identityref { + base SYSLOG_FACILITY; + } + description + "Specifies the facility, or class of messages to log"; + } + + leaf severity { + type syslog-severity; + description + "Specifies that only messages of the given severity (or + greater severity) for the corresonding facility are logged"; + } + } + + grouping logging-selectors-state { + description + "Operational state data for logging selectors"; + } + + grouping logging-selectors-top { + description + "Top-level grouping for the logging selector list"; + + container selectors { + description + "Enclosing container "; + + list selector { + key "facility severity"; + description + "List of selectors for log messages"; + + leaf facility { + type leafref { + path "../config/facility"; + } + description + "Reference to facility list key"; + } + + leaf severity { + type leafref { + path "../config/severity"; + } + description + "Reference to severity list key"; + } + + container config { + description + "Configuration data "; + + uses logging-selectors-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses logging-selectors-config; + uses logging-selectors-state; + } + } + } + } + + grouping logging-console-config { + description + "Configuration data for console logging"; + } + + grouping logging-console-state { + description + "Operational state data for console logging"; + } + + grouping logging-console-top { + description + "Top-level grouping for console logging data"; + + container console { + description + "Top-level container for data related to console-based + logging"; + + container config { + description + "Configuration data for console logging"; + + uses logging-console-config; + } + + container state { + + config false; + + description + "Operational state data for console logging"; + + uses logging-console-config; + uses logging-console-state; + } + + uses logging-selectors-top; + } + } + + grouping logging-remote-config { + description + "Configuration data for remote log servers"; + + leaf host { + type oc-inet:host; + description + "IP address or hostname of the remote log server"; + } + + leaf source-address { + type oc-inet:ip-address; + description + "Source IP address for packets to the log server"; + } + + leaf remote-port { + type oc-inet:port-number; + default 514; + description + "Sets the destination port number for syslog UDP messages to + the server. The default for syslog is 514."; + } + } + + grouping logging-remote-state { + description + "Operational state data for remote log servers"; + } + + grouping logging-remote-top { + description + "Top-level grouping for remote log servers"; + + container remote-servers { + description + "Enclosing container for the list of remote log servers"; + + list remote-server { + key "host"; + description + "List of remote log servers"; + + leaf host { + type leafref { + path "../config/host"; + } + description + "Reference to the host list key"; + } + + container config { + description + "Configuration data for remote log servers"; + + uses logging-remote-config; + } + + container state { + + config false; + + description + "Operational state data for remote log servers"; + + uses logging-remote-config; + uses logging-remote-state; + } + uses logging-selectors-top; + } + } + } + + grouping logging-top { + description + "Top-level grouping for logging data"; + + container logging { + description + "Top-level container for data related to logging / syslog"; + + uses logging-console-top; + uses logging-remote-top; + } + } + // data definition statements + + // augment statements + + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-system-management.yang b/models/yang/common/openconfig-system-management.yang new file mode 100644 index 0000000000..00494b7a76 --- /dev/null +++ b/models/yang/common/openconfig-system-management.yang @@ -0,0 +1,138 @@ +module openconfig-system-management { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/system/management"; + + prefix "oc-sys-mgmt"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + import openconfig-inet-types { prefix oc-inet; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + related to management services."; + + oc-ext:openconfig-version "0.1.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.1.2"; + } + + revision "2018-08-28" { + description + "Update description of the ANY enum."; + reference "0.1.1"; + } + + revision "2018-07-26" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + // typedef statements + + // grouping statements + + grouping system-grpc-server-config { + description + "Configuration data for the gRPC server"; + + leaf enable { + type boolean; + default true; + description + "Enables the gRPC server. The gRPC server is enabled by + default"; + } + + leaf port { + type oc-inet:port-number; + default 9339; + description + "TCP port on which the gRPC server should listen"; + } + + leaf transport-security { + type boolean; + description + "Enables gRPC transport security (e.g., TLS or SSL)"; + } + + leaf certificate-id { + type string; + description + "The certificate ID to be used for authentication"; + } + + leaf-list listen-addresses { + type union { + type oc-inet:ip-address; + type enumeration { + enum ANY { + description + "The gRPC daemon should listen on any address + bound to an interface on the system."; + } + } + } + description + "The IP addresses that the gRPC server should listen + on. This may be an IPv4 or an IPv6 address"; + } + } + + grouping system-grpc-server-top { + description + "Top-level grouping for system gRPC server data"; + + container grpc-server { + description + "Top-level container for the gRPC server"; + + container config { + description + "Configuration data for the system gRPC server"; + + uses system-grpc-server-config; + } + + container state { + config false; + + description + "Operational state data for the system gRPC server"; + + uses system-grpc-server-config; + } + } + } + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} diff --git a/models/yang/common/openconfig-system-terminal.yang b/models/yang/common/openconfig-system-terminal.yang new file mode 100644 index 0000000000..b34811c999 --- /dev/null +++ b/models/yang/common/openconfig-system-terminal.yang @@ -0,0 +1,249 @@ +module openconfig-system-terminal { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/system/terminal"; + + prefix "oc-sys-term"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + related to remote terminal services such as ssh and telnet."; + + oc-ext:openconfig-version "0.3.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.3.1"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + // typedef statements + + // grouping statements + + grouping system-terminal-common-config { + description + "Common configuration data for terminal services"; + + leaf timeout { + type uint16; + units seconds; + description + "Set the idle timeout in seconds on terminal connections to + the system for the protocol."; + } + + leaf rate-limit { + type uint16; + units "conn/min"; + description + "Set a limit on the number of connection attempts per + minute to the system for the protocol."; + } + + leaf session-limit { + type uint16; + description + "Set a limit on the number of simultaneous active terminal + sessions to the system for the protocol (e.g., ssh, + telnet, ...) "; + } + } + + grouping system-terminal-common-state { + description + "Common operational state data for terminal services"; + } + + grouping system-terminal-common-top { + description + "Top-level grouping for common terminal service data"; + + container terminal-servers { + description + "Top-level container for terminal services"; + + container config { + description + "Configuration data for terminal services"; + + uses system-terminal-common-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses system-terminal-common-config; + uses system-terminal-common-state; + } + } + } + + grouping system-ssh-server-config { + description + "Configuration data for system ssh configuration"; + + leaf enable { + type boolean; + default true; + description + "Enables the ssh server. The ssh server is enabled by + default."; + } + + leaf protocol-version { + type enumeration { + enum V2 { + description + "Use SSH v2 only"; + } + enum V1 { + description + "Use SSH v1 only"; + } + enum V1_V2 { + description + "Use either SSH v1 or v2"; + } + } + default V2; + description + "Set the protocol version for SSH connections to the system"; + } + + uses system-terminal-common-config; + } + + grouping system-ssh-server-state { + description + "Operational state data for ssh server"; + } + + grouping system-ssh-server-top { + description + "Top-level grouping for ssh server data"; + + container ssh-server { + description + "Top-level container for ssh server"; + + container config { + description + "Configuration data for the system ssh server"; + + uses system-ssh-server-config; + } + + container state { + + config false; + + description + "Operational state data for the system ssh server"; + + uses system-ssh-server-config; + uses system-ssh-server-state; + } + } + } + + grouping system-telnet-server-config { + description + "Configuration data for telnet server"; + + leaf enable { + type boolean; + default false; + description + "Enables the telnet server. Telnet is disabled by + default"; + } + uses system-terminal-common-config; + + } + + grouping system-telnet-server-state { + description + "Operational state data for telnet server"; + } + + grouping system-telnet-server-top { + description + "Top-level grouping for telnet server "; + + container telnet-server { + description + "Top-level container for telnet terminal servers"; + + container config { + description + "Configuration data for telnet"; + + uses system-telnet-server-config; + } + + container state { + + config false; + + description + "Operational state data for telnet"; + + uses system-telnet-server-config; + uses system-telnet-server-state; + } + } + } + + // data definition statements + + // augment statements + + // rpc statements + + // notification statements + +} \ No newline at end of file diff --git a/models/yang/common/openconfig-types.yang b/models/yang/common/openconfig-types.yang new file mode 100644 index 0000000000..a7ba45d91b --- /dev/null +++ b/models/yang/common/openconfig-types.yang @@ -0,0 +1,466 @@ +module openconfig-types { + yang-version "1"; + + namespace "http://openconfig.net/yang/openconfig-types"; + + prefix "oc-types"; + + // import statements + import openconfig-extensions { prefix oc-ext; } + + // meta + organization + "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module contains a set of general type definitions that + are used across OpenConfig models. It can be imported by modules + that make use of these types."; + + oc-ext:openconfig-version "0.5.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.5.1"; + } + + revision "2018-05-05" { + description + "Add grouping of min-max-time and + included them to all stats with min/max/avg"; + reference "0.5.0"; + } + + revision "2018-01-16" { + description + "Add interval to min/max/avg stats; add percentage stat"; + reference "0.4.0"; + } + + revision "2017-08-16" { + description + "Apply fix for ieetfloat32 length parameter"; + reference "0.3.3"; + } + + revision "2017-01-13" { + description + "Add ADDRESS_FAMILY identity"; + reference "0.3.2"; + } + + revision "2016-11-14" { + description + "Correct length of ieeefloat32"; + reference "0.3.1"; + } + + revision "2016-11-11" { + description + "Additional types - ieeefloat32 and routing-password"; + reference "0.3.0"; + } + + revision "2016-05-31" { + description + "OpenConfig public release"; + reference "0.2.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + typedef percentage { + type uint8 { + range "0..100"; + } + description + "Integer indicating a percentage value"; + } + + typedef std-regexp { + type string; + description + "This type definition is a placeholder for a standard + definition of a regular expression that can be utilised in + OpenConfig models. Further discussion is required to + consider the type of regular expressions that are to be + supported. An initial proposal is POSIX compatible."; + } + + typedef timeticks64 { + type uint64; + description + "This type is based on the timeticks type defined in + RFC 6991, but with 64-bit width. It represents the time, + modulo 2^64, in hundredths of a second between two epochs."; + reference + "RFC 6991 - Common YANG Data Types"; + } + + typedef ieeefloat32 { + type binary { + length "4"; + } + description + "An IEEE 32-bit floating point number. The format of this number + is of the form: + 1-bit sign + 8-bit exponent + 23-bit fraction + The floating point value is calculated using: + (-1)**S * 2**(Exponent-127) * (1+Fraction)"; + } + + typedef routing-password { + type string; + description + "This type is indicative of a password that is used within + a routing protocol which can be returned in plain text to the + NMS by the local system. Such passwords are typically stored + as encrypted strings. Since the encryption used is generally + well known, it is possible to extract the original value from + the string - and hence this format is not considered secure. + Leaves specified with this type should not be modified by + the system, and should be returned to the end-user in plain + text. This type exists to differentiate passwords, which + may be sensitive, from other string leaves. It could, for + example, be used by the NMS to censor this data when + viewed by particular users."; + } + + typedef stat-interval { + type uint64; + units nanoseconds; + description + "A time interval over which a set of statistics is computed. + A common usage is to report the interval over which + avg/min/max stats are computed and reported."; + } + + grouping stat-interval-state { + description + "Reusable leaf definition for stats computation interval"; + + leaf interval { + type oc-types:stat-interval; + description + "If supported by the system, this reports the time interval + over which the min/max/average statistics are computed by + the system."; + } + } + + grouping min-max-time { + description + "Common grouping for recording the absolute time at which + the minimum and maximum values occurred in the statistics"; + + leaf min-time { + type oc-types:timeticks64; + description + "The absolute time at which the minimum value occurred. + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + leaf max-time { + type oc-types:timeticks64; + description + "The absolute time at which the maximum value occurred. + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + } + + grouping avg-min-max-stats-precision1 { + description + "Common nodes for recording average, minimum, and + maximum values for a statistic. These values all have + fraction-digits set to 1. Statistics are computed + and reported based on a moving time interval (e.g., the last + 30s). If supported by the device, the time interval over which + the statistics are computed is also reported."; + + leaf avg { + type decimal64 { + fraction-digits 1; + } + description + "The arithmetic mean value of the statistic over the + time interval."; + } + + leaf min { + type decimal64 { + fraction-digits 1; + } + description + "The minimum value of the statistic over the time + interval."; + } + + leaf max { + type decimal64 { + fraction-digits 1; + } + description + "The maximum value of the statitic over the time + interval."; + } + + uses stat-interval-state; + uses min-max-time; + } + + grouping avg-min-max-instant-stats-precision1 { + description + "Common grouping for recording an instantaneous statistic value + in addition to avg-min-max stats"; + + leaf instant { + type decimal64 { + fraction-digits 1; + } + description + "The instantaneous value of the statistic."; + } + + uses avg-min-max-stats-precision1; + } + + grouping avg-min-max-instant-stats-precision2-dB { + description + "Common grouping for recording dB values with 2 decimal + precision. Values include the instantaneous, average, + minimum, and maximum statistics. Statistics are computed + and reported based on a moving time interval (e.g., the last + 30s). If supported by the device, the time interval over which + the statistics are computed, and the times at which the minimum + and maximum values occurred, are also reported."; + + leaf instant { + type decimal64 { + fraction-digits 2; + } + units dB; + description + "The instantaneous value of the statistic."; + } + + leaf avg { + type decimal64 { + fraction-digits 2; + } + units dB; + description + "The arithmetic mean value of the statistic over the + time interval."; + } + + leaf min { + type decimal64 { + fraction-digits 2; + } + units dB; + description + "The minimum value of the statistic over the time interval."; + } + + leaf max { + type decimal64 { + fraction-digits 2; + } + units dB; + description + "The maximum value of the statistic over the time + interval."; + } + + uses stat-interval-state; + uses min-max-time; + } + + grouping avg-min-max-instant-stats-precision2-dBm { + description + "Common grouping for recording dBm values with 2 decimal + precision. Values include the instantaneous, average, + minimum, and maximum statistics. Statistics are computed + and reported based on a moving time interval (e.g., the last + 30s). If supported by the device, the time interval over which + the statistics are computed, and the times at which the minimum + and maximum values occurred, are also reported."; + + leaf instant { + type decimal64 { + fraction-digits 2; + } + units dBm; + description + "The instantaneous value of the statistic."; + } + + leaf avg { + type decimal64 { + fraction-digits 2; + } + units dBm; + description + "The arithmetic mean value of the statistic over the + time interval."; + } + + leaf min { + type decimal64 { + fraction-digits 2; + } + units dBm; + description + "The minimum value of the statistic over the time + interval."; + } + + leaf max { + type decimal64 { + fraction-digits 2; + } + units dBm; + description + "The maximum value of the statistic over the time interval."; + } + + uses stat-interval-state; + uses min-max-time; + } + + grouping avg-min-max-instant-stats-precision2-mA { + description + "Common grouping for recording mA values with 2 decimal + precision. Values include the instantaneous, average, + minimum, and maximum statistics. Statistics are computed + and reported based on a moving time interval (e.g., the last + 30s). If supported by the device, the time interval over which + the statistics are computed, and the times at which the minimum + and maximum values occurred, are also reported."; + + leaf instant { + type decimal64 { + fraction-digits 2; + } + units mA; + description + "The instantaneous value of the statistic."; + } + + leaf avg { + type decimal64 { + fraction-digits 2; + } + units mA; + description + "The arithmetic mean value of the statistic over the + time interval."; + } + + leaf min { + type decimal64 { + fraction-digits 2; + } + units mA; + description + "The minimum value of the statistic over the time + interval."; + } + + leaf max { + type decimal64 { + fraction-digits 2; + } + units mA; + description + "The maximum value of the statistic over the time + interval."; + } + + uses stat-interval-state; + uses min-max-time; + } + + grouping avg-min-max-instant-stats-pct { + description + "Common grouping for percentage statistics. + Values include the instantaneous, average, + minimum, and maximum statistics. Statistics are computed + and reported based on a moving time interval (e.g., the last + 30s). If supported by the device, the time interval over which + the statistics are computed, and the times at which the minimum + and maximum values occurred, are also reported."; + + leaf instant { + type oc-types:percentage; + description + "The instantaneous percentage value."; + } + + leaf avg { + type oc-types:percentage; + description + "The arithmetic mean value of the percentage measure of the + statistic over the time interval."; + } + + leaf min { + type oc-types:percentage; + description + "The minimum value of the percentage measure of the + statistic over the time interval."; + } + + leaf max { + type oc-types:percentage; + description + "The maximum value of the percentage measure of the + statistic over the time interval."; + } + + uses stat-interval-state; + uses min-max-time; + } + + identity ADDRESS_FAMILY { + description + "A base identity for all address families"; + } + + identity IPV4 { + base ADDRESS_FAMILY; + description + "The IPv4 address family"; + } + + identity IPV6 { + base ADDRESS_FAMILY; + description + "The IPv6 address family"; + } + + identity MPLS { + base ADDRESS_FAMILY; + description + "The MPLS address family"; + } + + identity L2_ETHERNET { + base ADDRESS_FAMILY; + description + "The 802.3 Ethernet address family"; + } + +} diff --git a/models/yang/common/openconfig-vlan-types.yang b/models/yang/common/openconfig-vlan-types.yang new file mode 100644 index 0000000000..7778e19f0b --- /dev/null +++ b/models/yang/common/openconfig-vlan-types.yang @@ -0,0 +1,206 @@ +module openconfig-vlan-types { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/vlan-types"; + + prefix "oc-vlan-types"; + + // import some basic types + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module defines configuration and state variables for VLANs, + in addition to VLAN parameters associated with interfaces"; + + oc-ext:openconfig-version "3.0.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.1"; + } + + revision "2018-02-14" { + description + "Fix bug with name of 802.1ad identity."; + reference "3.0.0"; + } + + revision "2017-07-14" { + description + "Move top-level vlan data to network-instance; Update + identities to comply to style guide; fixed pattern + quoting; corrected trunk vlan types; added TPID config to + base interface."; + reference "2.0.0"; + } + + revision "2016-05-26" { + description + "OpenConfig public release"; + reference "1.0.2"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // extension statements + + // feature statements + + // identity statements + + identity TPID_TYPES { + description + "Base identity for TPID values that can override the VLAN + ethertype value"; + } + + identity TPID_0X8100 { + base TPID_TYPES; + description + "Default TPID value for 802.1q single-tagged VLANs."; + } + + identity TPID_0X88A8 { + base TPID_TYPES; + description + "TPID value for 802.1ad provider bridging, QinQ or + stacked VLANs."; + } + + identity TPID_0X9100 { + base TPID_TYPES; + description + "Alternate TPID value"; + } + + identity TPID_0X9200 { + base TPID_TYPES; + description + "Alternate TPID value"; + } + + // typedef statements + + // TODO: typedefs should be defined in a vlan-types.yang file. + typedef vlan-id { + type uint16 { + range 1..4094; + } + description + "Type definition representing a single-tagged VLAN"; + } + + typedef vlan-range { + type string { + // range specified as [lower]..[upper] + pattern '^(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])\.\.(409[0-4]|' + + '40[0-8][0-9]|[1-3][0-9]{3}|[1-9][0-9]{1,2}|' + + '[1-9])$'; + } + description + "Type definition representing a range of single-tagged + VLANs. A range is specified as x..y where x and y are + valid VLAN IDs (1 <= vlan-id <= 4094). The range is + assumed to be inclusive, such that any VLAN-ID matching + x <= VLAN-ID <= y falls within the range."; + } + + typedef qinq-id { + type string { + pattern + '^(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])\.' + + '((409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])|\*)$'; + } + description + "Type definition representing a single double-tagged/QinQ VLAN + identifier. The format of a QinQ VLAN-ID is x.y where X is the + 'outer' VLAN identifier, and y is the 'inner' VLAN identifier. + Both x and y must be valid VLAN IDs (1 <= vlan-id <= 4094) + with the exception that y may be equal to a wildcard (*). In + cases where y is set to the wildcard, this represents all inner + VLAN identifiers where the outer VLAN identifier is equal to + x"; + } + + typedef qinq-id-range { + type union { + type string { + // match cases where the range is specified as x..y.z + pattern + '^(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])\.\.' + + '(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])\.' + + '((409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])|\*)$'; + } + type string { + // match cases where the range is specified as x.y..z + pattern + '^(\*|(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9]))\.' + + '(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])\.\.' + + '(409[0-4]|40[0-8][0-9]|[1-3][0-9]{3}|' + + '[1-9][0-9]{1,2}|[1-9])$'; + } + } + description + "A type definition representing a range of double-tagged/QinQ + VLAN identifiers. The format of a QinQ VLAN-ID range can be + specified in three formats. Where the range is outer VLAN IDs + the range is specified as x..y.z. In this case outer VLAN + identifiers meeting the criteria x <= outer-vlan-id <= y are + accepted iff the inner VLAN-ID is equal to y - or any inner-tag + if the wildcard is specified. Alternatively the range can be + specified as x.y..z. In this case only VLANs with an + outer-vlan-id qual to x are accepted (x may again be the + wildcard). Inner VLANs are accepted if they meet the inequality + y <= inner-vlan-id <= z."; + } + + typedef vlan-mode-type { + type enumeration { + enum ACCESS { + description "Access mode VLAN interface (No 802.1q header)"; + } + enum TRUNK { + description "Trunk mode VLAN interface"; + } + } + description + "VLAN interface mode (trunk or access)"; + } + + typedef vlan-ref { + type union { + type vlan-id; + type string; + // TODO: string should be changed to leafref to reference + // an existing VLAN. this is not allowed in YANG 1.0 but + // is expected to be in YANG 1.1. + // type leafref { + // path "vlan:vlans/vlan:vlan/vlan:config/vlan:name"; + // } + } + description + "Reference to a VLAN by name or id"; + } + +} diff --git a/models/yang/common/openconfig-vlan.yang b/models/yang/common/openconfig-vlan.yang new file mode 100644 index 0000000000..1cae824760 --- /dev/null +++ b/models/yang/common/openconfig-vlan.yang @@ -0,0 +1,449 @@ +module openconfig-vlan { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/vlan"; + + prefix "oc-vlan"; + + // import some basic types + import openconfig-vlan-types { prefix oc-vlan-types; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-if-ethernet { prefix oc-eth; } + import openconfig-if-aggregate { prefix oc-lag; } + import iana-if-type { prefix ift; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This module defines configuration and state variables for VLANs, + in addition to VLAN parameters associated with interfaces"; + + oc-ext:openconfig-version "3.0.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "3.0.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements."; + reference "3.0.1"; + } + + revision "2018-02-14" { + description + "Fix bug with name of 802.1ad identity."; + reference "3.0.0"; + } + + revision "2017-07-14" { + description + "Move top-level vlan data to network-instance; Update + identities to comply to style guide; fixed pattern + quoting; corrected trunk vlan types; added TPID config to + base interface."; + reference "2.0.0"; + } + + revision "2016-05-26" { + description + "OpenConfig public release"; + reference "1.0.2"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + grouping vlan-config { + description "VLAN configuration container."; + + leaf vlan-id { + type oc-vlan-types:vlan-id; + description "Interface VLAN id."; + } + + leaf name { + type string; + description "Interface VLAN name."; + } + + leaf status { + type enumeration { + enum ACTIVE { + description "VLAN is active"; + } + enum SUSPENDED { + description "VLAN is inactive / suspended"; + } + } + default ACTIVE; + description "Admin state of the VLAN"; + } + + } + + grouping vlan-state { + description "State variables for VLANs"; + + // placeholder + + } + + grouping vlan-tpid-config { + description + "TPID configuration for dot1q-enabled interfaces"; + + leaf tpid { + type identityref { + base oc-vlan-types:TPID_TYPES; + } + default oc-vlan-types:TPID_0X8100; + description + "Optionally set the tag protocol identifier field (TPID) that + is accepted on the VLAN"; + } + } + + grouping vlan-tpid-state { + description + "TPID opstate for dot1q-enabled interfaces"; + + // placeholder + + } + + grouping vlan-members-state { + description + "List of interfaces / subinterfaces belonging to the VLAN."; + + container members { + description + "Enclosing container for list of member interfaces"; + + list member { + config false; + description + "List of references to interfaces / subinterfaces + associated with the VLAN."; + + uses oc-if:base-interface-ref-state; + } + } + } + + grouping vlan-switched-config { + description + "VLAN related configuration that is part of the physical + Ethernet interface."; + + leaf interface-mode { + type oc-vlan-types:vlan-mode-type; + description + "Set the interface to access or trunk mode for + VLANs"; + } + + leaf native-vlan { + when "../interface-mode = 'TRUNK'" { + description + "Native VLAN is valid for trunk mode interfaces"; + } + type oc-vlan-types:vlan-id; + description + "Set the native VLAN id for untagged frames arriving on + a trunk interface. Tagged frames sent on an interface + configured with a native VLAN should have their tags + stripped prior to transmission. This configuration is only + valid on a trunk interface."; + } + + leaf access-vlan { + when "../interface-mode = 'ACCESS'" { + description + "Access VLAN assigned to the interfaces"; + } + type oc-vlan-types:vlan-id; + description + "Assign the access vlan to the access port."; + } + + leaf-list trunk-vlans { + when "../interface-mode = 'TRUNK'" { + description + "Allowed VLANs may be specified for trunk mode + interfaces."; + } + type union { + type oc-vlan-types:vlan-id; + type oc-vlan-types:vlan-range; + } + description + "Specify VLANs, or ranges thereof, that the interface may + carry when in trunk mode. If not specified, all VLANs are + allowed on the interface. Ranges are specified in the form + x..y, where x + Editor: Yuanlong Jiang + + Editor: Rodney Cummings + "; + description + "This YANG module defines a data model for the configuration + of IEEE Std 1588-2008 clocks, and also for retrieval of the state + data of IEEE Std 1588-2008 clocks."; + + revision 2019-05-07 { + description + "Initial version"; + reference + "RFC 8575: YANG Data Model for the Precision Time Protocol"; + } + + typedef delay-mechanism-enumeration { + type enumeration { + enum e2e { + value 1; + description + "The port uses the delay request-response mechanism."; + } + enum p2p { + value 2; + description + "The port uses the peer delay mechanism."; + } + enum disabled { + value 254; + description + "The port does not implement any delay mechanism."; + } + } + description + "The propagation-delay measuring option used by the + port. Values for this enumeration are specified + by the IEEE Std 1588 standard exclusively."; + reference + "IEEE Std 1588-2008: 8.2.5.4.4"; + } + + typedef port-state-enumeration { + type enumeration { + enum initializing { + value 1; + description + "The port is initializing its data sets, hardware, and + communication facilities."; + } + enum faulty { + value 2; + description + "The port is in the fault state."; + } + enum disabled { + value 3; + description + "The port is disabled and is not communicating PTP + messages (other than possibly PTP management + messages)."; + } + enum listening { + value 4; + description + "The port is listening for an Announce message."; + } + enum pre-master { + value 5; + description + "The port is in the pre-master state."; + } + enum master { + value 6; + description + "The port is behaving as a master port."; + } + enum passive { + value 7; + description + "The port is in the passive state."; + } + enum uncalibrated { + value 8; + description + "A master port has been selected, but the port is still + in the uncalibrated state."; + } + enum slave { + value 9; + description + "The port is synchronizing to the selected master port."; + } + } + description + "The current state of the protocol engine associated + with the port. Values for this enumeration are specified + by the IEEE Std 1588 standard exclusively."; + reference + "IEEE Std 1588-2008: 8.2.5.3.1, 9.2.5"; + } + + typedef time-interval-type { + type int64; + description + "Derived data type for time interval, represented in units of + nanoseconds and multiplied by 2^16"; + reference + "IEEE Std 1588-2008: 5.3.2"; + } + + typedef clock-identity-type { + type binary { + length "8"; + } + description + "Derived data type to identify a clock"; + reference + "IEEE Std 1588-2008: 5.3.4"; + } + + grouping clock-quality-grouping { + description + "Derived data type for quality of a clock, which contains + clockClass, clockAccuracy, and offsetScaledLogVariance."; + reference + "IEEE Std 1588-2008: 5.3.7"; + leaf clock-class { + type uint8; + default "248"; + description + "The clockClass denotes the traceability of the time + or frequency distributed by the clock."; + } + leaf clock-accuracy { + type uint8; + description + "The clockAccuracy indicates the expected accuracy + of the clock."; + } + leaf offset-scaled-log-variance { + type uint16; + description + "The offsetScaledLogVariance provides an estimate of + the variations of the clock from a linear timescale + when it is not synchronized to another clock + using the protocol."; + } + } + + container ptp { + description + "The PTP struct containing all attributes of PTP data set, + other optional PTP attributes can be augmented as well."; + list instance-list { + key "instance-number"; + description + "List of one or more PTP data sets in the device (see IEEE + Std 1588-2008 subclause 6.3). + Each PTP data set represents a distinct instance of + PTP implementation in the device (i.e., distinct + Ordinary Clock or Boundary Clock)."; + leaf instance-number { + type uint32; + description + "The instance number of the current PTP instance. + This instance number is used for management purposes + only. This instance number does not represent the PTP + domain number and is not used in PTP messages."; + } + container default-ds { + description + "The default data set of the clock (see IEEE Std + 1588-2008 subclause 8.2.1). This data set represents + the configuration/state required for operation + of Precision Time Protocol (PTP) state machines."; + reference + "IEEE Std 1588-2008: 8.2.1"; + leaf two-step-flag { + type boolean; + description + "When set to true, the clock is a two-step clock; + otherwise,the clock is a one-step clock."; + } + leaf clock-identity { + type clock-identity-type; + config false; + description + "The clockIdentity of the local clock."; + } + leaf number-ports { + type uint16; + description + "The number of PTP ports on the instance."; + } + container clock-quality { + description + "The clockQuality of the local clock."; + uses clock-quality-grouping; + } + leaf priority1 { + type uint8; + description + "The priority1 attribute of the local clock."; + } + leaf priority2 { + type uint8; + description + "The priority2 attribute of the local clock."; + } + leaf domain-number { + type uint8; + description + "The domain number of the current syntonization + domain."; + } + leaf slave-only { + type boolean; + description + "When set to true, the clock is a slave-only clock."; + } + } + container current-ds { + description + "The current data set of the clock (see IEEE Std + 1588-2008 subclause 8.2.2). This data set represents + local states learned from the exchange of + Precision Time Protocol (PTP) messages."; + reference + "IEEE Std 1588-2008: 8.2.2"; + leaf steps-removed { + type uint16; + default "0"; + description + "The number of communication paths traversed + between the local clock and the grandmaster clock."; + } + leaf offset-from-master { + type time-interval-type; + description + "The current value of the time difference between + a master and a slave clock as computed by the slave."; + } + leaf mean-path-delay { + type time-interval-type; + description + "The current value of the mean propagation time between + a master and a slave clock as computed by the slave."; + } + } + container parent-ds { + description + "The parent data set of the clock (see IEEE Std 1588-2008 + subclause 8.2.3)."; + reference + "IEEE Std 1588-2008: 8.2.3"; + container parent-port-identity { + description + "The portIdentity of the port on the master, it + contains two members: clockIdentity and portNumber."; + reference + "IEEE Std 1588-2008: 5.3.5"; + leaf clock-identity { + type clock-identity-type; + + description + "Identity of the clock."; + } + leaf port-number { + type uint16; + description + "Port number."; + } + } + leaf parent-stats { + type boolean; + default "false"; + description + "When set to true, the values of + observedParentOffsetScaledLogVariance and + observedParentClockPhaseChangeRate of parentDS + have been measured and are valid."; + } + leaf observed-parent-offset-scaled-log-variance { + type uint16; + default "65535"; + description + "An estimate of the parent clock's PTP variance + as observed by the slave clock."; + } + leaf observed-parent-clock-phase-change-rate { + type int32; + description + "An estimate of the parent clock's phase change rate + as observed by the slave clock."; + } + leaf grandmaster-identity { + type clock-identity-type; + description + "The clockIdentity attribute of the grandmaster clock."; + } + container grandmaster-clock-quality { + description + "The clockQuality of the grandmaster clock."; + uses clock-quality-grouping; + } + leaf grandmaster-priority1 { + type uint8; + description + "The priority1 attribute of the grandmaster clock."; + } + leaf grandmaster-priority2 { + type uint8; + description + "The priority2 attribute of the grandmaster clock."; + } + } + container time-properties-ds { + description + "The timeProperties data set of the clock (see + IEEE Std 1588-2008 subclause 8.2.4)."; + reference + "IEEE Std 1588-2008: 8.2.4"; + leaf current-utc-offset-valid { + type boolean; + description + "When set to true, the current UTC offset is valid."; + } + leaf current-utc-offset { + when "../current-utc-offset-valid='true'"; + type int16; + description + "The offset between TAI and UTC when the epoch of the + PTP system is the PTP epoch in units of seconds, i.e., + when ptp-timescale is TRUE; otherwise, the value has + no meaning."; + } + leaf leap59 { + type boolean; + description + "When set to true, the last minute of the current UTC + day contains 59 seconds."; + } + leaf leap61 { + type boolean; + description + "When set to true, the last minute of the current UTC + day contains 61 seconds."; + } + leaf time-traceable { + type boolean; + description + "When set to true, the timescale and the + currentUtcOffset are traceable to a primary + reference."; + } + leaf frequency-traceable { + type boolean; + description + "When set to true, the frequency determining the + timescale is traceable to a primary reference."; + } + leaf ptp-timescale { + type boolean; + description + "When set to true, the clock timescale of the + grandmaster clock is PTP; otherwise, the timescale is + ARB (arbitrary)."; + } + leaf time-source { + type uint8; + description + "The source of time used by the grandmaster clock."; + } + } + list port-ds-list { + key "port-number"; + description + "List of port data sets of the clock (see IEEE Std + 1588-2008 subclause 8.2.5)."; + reference + "IEEE Std 1588-2008: 8.2.5"; + leaf port-number { + type uint16; + description + "Port number. + The data sets (i.e., information model) of IEEE Std + 1588-2008 specify a member portDS.portIdentity, which + uses a typed struct with members clockIdentity and + portNumber. + + In this YANG data model, portIdentity is not modeled + in the port-ds-list. However, its members are provided + as follows: + portIdentity.portNumber is provided as this + port-number leaf in port-ds-list, and + portIdentity.clockIdentity is provided as the + clock-identity leaf in default-ds of the instance + (i.e., ../../default-ds/clock-identity)."; + } + leaf port-state { + type port-state-enumeration; + default "initializing"; + description + "Current state associated with the port."; + } + leaf underlying-interface { + type if:interface-ref; + + description + "Reference to the configured underlying interface that + is used by this PTP port (see RFC 8343)."; + reference + "RFC 8343: A YANG Data Model for Interface Management"; + } + leaf log-min-delay-req-interval { + type int8; + description + "The base-2 logarithm of the minDelayReqInterval + (the minimum permitted mean time interval between + successive Delay_Req messages)."; + } + leaf peer-mean-path-delay { + type time-interval-type; + default "0"; + description + "An estimate of the current one-way propagation delay + on the link when the delayMechanism is P2P; otherwise, + it is zero."; + } + leaf log-announce-interval { + type int8; + description + "The base-2 logarithm of the mean + announceInterval (mean time interval between + successive Announce messages)."; + } + leaf announce-receipt-timeout { + type uint8; + description + "The number of announceIntervals that have to pass + without receipt of an Announce message before the + occurrence of the event ANNOUNCE_RECEIPT_TIMEOUT_ + EXPIRES."; + } + leaf log-sync-interval { + type int8; + description + "The base-2 logarithm of the mean SyncInterval + for multicast messages. The rates for unicast + transmissions are negotiated separately on a per-port + basis and are not constrained by this attribute."; + } + leaf delay-mechanism { + type delay-mechanism-enumeration; + + description + "The propagation delay measuring option used by the + port in computing meanPathDelay."; + } + leaf log-min-pdelay-req-interval { + type int8; + description + "The base-2 logarithm of the + minPdelayReqInterval (minimum permitted mean time + interval between successive Pdelay_Req messages)."; + } + leaf version-number { + type uint8; + description + "The PTP version in use on the port."; + } + } + } + container transparent-clock-default-ds { + description + "The members of the transparentClockDefault data set (see + IEEE Std 1588-2008 subclause 8.3.2)."; + reference + "IEEE Std 1588-2008: 8.3.2"; + leaf clock-identity { + type clock-identity-type; + config false; + description + "The clockIdentity of the transparent clock."; + } + leaf number-ports { + type uint16; + description + "The number of PTP ports on the transparent clock."; + } + leaf delay-mechanism { + type delay-mechanism-enumeration; + description + "The propagation delay measuring option + used by the transparent clock."; + } + leaf primary-domain { + type uint8; + default "0"; + description + "The domainNumber of the primary syntonization domain (see + IEEE Std 1588-2008 subclause 10.1)."; + + reference + "IEEE Std 1588-2008: 10.1"; + } + } + list transparent-clock-port-ds-list { + key "port-number"; + description + "List of transparentClockPort data sets of the transparent + clock (see IEEE Std 1588-2008 subclause 8.3.3)."; + reference + "IEEE Std 1588-2008: 8.3.3"; + leaf port-number { + type uint16; + description + "Port number. + The data sets (i.e., information model) of IEEE Std + 1588-2008 specify a member + transparentClockPortDS.portIdentity, which uses a typed + struct with members clockIdentity and portNumber. + + In this YANG data model, portIdentity is not modeled in + the transparent-clock-port-ds-list. However, its + members are provided as follows: + portIdentity.portNumber is provided as this leaf member + in transparent-clock-port-ds-list and + portIdentity.clockIdentity is provided as the + clock-identity leaf in transparent-clock-default-ds + (i.e., ../../transparent-clock-default-ds/clock- + identity)."; + } + leaf log-min-pdelay-req-interval { + type int8; + description + "The logarithm to the base 2 of the + minPdelayReqInterval (minimum permitted mean time + interval between successive Pdelay_Req messages)."; + } + leaf faulty-flag { + type boolean; + default "false"; + description + "When set to true, the port is faulty."; + } + leaf peer-mean-path-delay { + type time-interval-type; + default "0"; + + description + "An estimate of the current one-way propagation delay + on the link when the delayMechanism is P2P; otherwise, + it is zero."; + } + } + } +} diff --git a/models/yang/openconfig-acl.yang b/models/yang/openconfig-acl.yang new file mode 100644 index 0000000000..67c75ff47c --- /dev/null +++ b/models/yang/openconfig-acl.yang @@ -0,0 +1,846 @@ +module openconfig-acl { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/acl"; + + prefix "oc-acl"; + + import openconfig-packet-match { prefix oc-match; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state + data for network access control lists (i.e., filters, rules, + etc.). ACLs are organized into ACL sets, with each set + containing one or more ACL entries. ACL sets are identified + by a unique name, while each entry within a set is assigned + a sequence-id that determines the order in which the ACL + rules are applied to a packet. Note that ACLs are evaluated + in ascending order based on the sequence-id (low to high). + + Individual ACL rules specify match criteria based on fields in + the packet, along with an action that defines how matching + packets should be handled. Entries have a type that indicates + the type of match criteria, e.g., MAC layer, IPv4, IPv6, etc."; + + oc-ext:openconfig-version "1.0.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "1.0.2"; + } + + revision "2018-04-24" { + description + "Clarified order of ACL evaluation"; + reference "1.0.1"; + } + + revision "2017-05-26" { + description + "Separated ACL entries by type"; + reference "1.0.0"; + } + + revision "2016-08-08" { + description + "OpenConfig public release"; + reference "0.2.0"; + } + + revision "2016-01-22" { + description + "Initial revision"; + reference "TBD"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + identity ACL_TYPE { + description + "Base identity for types of ACL sets"; + } + + identity ACL_IPV4 { + base ACL_TYPE; + description + "IP-layer ACLs with IPv4 addresses"; + } + + identity ACL_IPV6 { + base ACL_TYPE; + description + "IP-layer ACLs with IPv6 addresses"; + } + + identity ACL_L2 { + base ACL_TYPE; + description + "MAC-layer ACLs"; + } + + identity ACL_MIXED { + base ACL_TYPE; + description + "Mixed-mode ACL that specifies L2 and L3 protocol + fields. This ACL type is not implemented by many + routing/switching devices."; + } + + // ACL action type + + identity FORWARDING_ACTION { + description + "Base identity for actions in the forwarding category"; + } + + identity ACCEPT { + base FORWARDING_ACTION; + description + "Accept the packet"; + } + + identity DROP { + base FORWARDING_ACTION; + description + "Drop packet without sending any ICMP error message"; + } + + identity REJECT { + base FORWARDING_ACTION; + description + "Drop the packet and send an ICMP error message to the source"; + } + + identity LOG_ACTION { + description + "Base identity for defining the destination for logging + actions"; + } + + identity LOG_SYSLOG { + base LOG_ACTION; + description + "Log the packet in Syslog"; + } + + identity LOG_NONE { + base LOG_ACTION; + description + "No logging"; + } + + identity ACL_COUNTER_CAPABILITY { + description + "Base identity for system to indicate how it is able to report + counters"; + } + + identity INTERFACE_ONLY { + base ACL_COUNTER_CAPABILITY; + description + "ACL counters are available and reported only per interface"; + } + + identity AGGREGATE_ONLY { + base ACL_COUNTER_CAPABILITY; + description + "ACL counters are aggregated over all interfaces, and reported + only per ACL entry"; + } + + identity INTERFACE_AGGREGATE { + base ACL_COUNTER_CAPABILITY; + description + "ACL counters are reported per interface, and also aggregated + and reported per ACL entry."; + } + + // grouping statements + + // input interface + grouping input-interface-config { + description + "Config of interface"; + + } + + grouping input-interface-state { + description + "State information of interface"; + } + + grouping input-interface-top { + description + "Input interface top level container"; + + container input-interface { + description + "Input interface container"; + + container config { + description + "Config data"; + uses input-interface-config; + } + + container state { + config false; + description + "State information"; + uses input-interface-config; + uses input-interface-state; + } + + uses oc-if:interface-ref; + + } + } + + // Action Type + grouping action-config { + description + "Config of action type"; + + + leaf forwarding-action { + type identityref { + base FORWARDING_ACTION; + } + mandatory true; + description + "Specifies the forwarding action. One forwarding action + must be specified for each ACL entry"; + } + + leaf log-action { + type identityref { + base LOG_ACTION; + } + default LOG_NONE; + description + "Specifies the log action and destination for + matched packets. The default is not to log the + packet."; + } + + + } + + grouping action-state { + description + "State information of action type"; + + } + + grouping action-top { + description + "ACL action type top level container"; + + container actions { + description + "Enclosing container for list of ACL actions associated + with an entry"; + + container config { + description + "Config data for ACL actions"; + uses action-config; + } + + container state { + config false; + description + "State information for ACL actions"; + uses action-config; + uses action-state; + } + } + } + + grouping acl-counters-state { + description + "Common grouping for ACL counters"; + + leaf matched-packets { + type oc-yang:counter64; + description + "Count of the number of packets matching the current ACL + entry. + + An implementation should provide this counter on a + per-interface per-ACL-entry if possible. + + If an implementation only supports ACL counters per entry + (i.e., not broken out per interface), then the value + should be equal to the aggregate count across all interfaces. + + An implementation that provides counters per entry per + interface is not required to also provide an aggregate count, + e.g., per entry -- the user is expected to be able implement + the required aggregation if such a count is needed."; + } + + leaf matched-octets { + type oc-yang:counter64; + description + "Count of the number of octets (bytes) matching the current + ACL entry. + + An implementation should provide this counter on a + per-interface per-ACL-entry if possible. + + If an implementation only supports ACL counters per entry + (i.e., not broken out per interface), then the value + should be equal to the aggregate count across all interfaces. + + An implementation that provides counters per entry per + interface is not required to also provide an aggregate count, + e.g., per entry -- the user is expected to be able implement + the required aggregation if such a count is needed."; + } + + } + + // Access List Entries + grouping access-list-entries-config { + description + "Access List Entries (ACE) config."; + + leaf sequence-id { + type uint32; + description + "The sequence id determines the order in which ACL entries + are applied. The sequence id must be unique for each entry + in an ACL set. Target devices should apply the ACL entry + rules in ascending order determined by sequence id (low to + high), rather than the relying only on order in the list."; + } + + leaf description { + type string; + description + "A user-defined description, or comment, for this Access List + Entry."; + } + + } + + grouping access-list-entries-state { + description + "Access List Entries state."; + + uses acl-counters-state; + + } + + grouping access-list-entries-top { + description + "Access list entries to level container"; + + container acl-entries { + description + "Access list entries container"; + + list acl-entry { + key "sequence-id"; + description + "List of ACL entries comprising an ACL set"; + + leaf sequence-id { + type leafref { + path "../config/sequence-id"; + } + description + "references the list key"; + } + + container config { + description + "Access list entries config"; + uses access-list-entries-config; + } + + container state { + config false; + description + "State information for ACL entries"; + uses access-list-entries-config; + uses access-list-entries-state; + } + + uses oc-match:ethernet-header-top { + when "../../config/type='ACL_L2'" { + description + "MAC-layer fields are valid when the ACL type is L2"; + } + } + uses oc-match:ipv4-protocol-fields-top { + when "../../config/type='ACL_IPV4'" { + description + "IPv4-layer fields are valid when the ACL type is + IPv4"; + } + } + uses oc-match:ipv6-protocol-fields-top { + when "../../config/type='ACL_IPV6'" { + description + "IPv6-layer fields are valid when the ACL type is + IPv6"; + } + } + uses oc-match:transport-fields-top { + when "../../config/type='ACL_IPV6' or " + + "../../config/type='ACL_IPV4'" { + description + "Transport-layer fields are valid when specifying + L3 ACL types"; + } + } + uses input-interface-top; + + uses action-top; + } + } + } + + grouping acl-set-config { + description + "Access Control List config"; + + leaf name { + type string; + description + "The name of the access-list set"; + } + + leaf type { + type identityref { + base ACL_TYPE; + } + description + "The type determines the fields allowed in the ACL entries + belonging to the ACL set (e.g., IPv4, IPv6, etc.)"; + } + + leaf description { + type string; + description + "Description, or comment, for the ACL set"; + } + + } + + grouping acl-set-state { + description + "Access Control List state"; + } + + grouping acl-set-top { + description + "Access list entries variables top level container"; + + container acl-sets { + description + "Access list entries variables enclosing container"; + + list acl-set { + key "name type"; + description + "List of ACL sets, each comprising of a list of ACL + entries"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the name list key"; + } + + leaf type { + type leafref { + path "../config/type"; + } + description + "Reference to the type list key"; + } + + container config { + description + "Access list config"; + uses acl-set-config; + } + + container state { + config false; + description + "Access list state information"; + uses acl-set-config; + uses acl-set-state; + } + uses access-list-entries-top; + } + } + } + + grouping interface-acl-entries-config { + description + "Configuration data for per-interface ACLs"; + + } + + grouping interface-acl-entries-state { + description + "Operational state data for per-interface ACL entries"; + + leaf sequence-id { + type leafref { + path "/acl/acl-sets/" + + "acl-set[name=current()/../../../../set-name]" + + "[type=current()/../../../../type]/" + + "acl-entries/acl-entry/sequence-id"; + } + description + "Reference to an entry in the ACL set applied to an + interface"; + } + + uses acl-counters-state; + + } + + grouping interface-acl-entries-top { + description + "Top-level grouping for per-interface ACL entries"; + + container acl-entries { + config false; + description + "Enclosing container for list of references to ACLs"; + + list acl-entry { + key "sequence-id"; + description + "List of ACL entries assigned to an interface"; + + leaf sequence-id { + type leafref { + path "../state/sequence-id"; + } + description + "Reference to per-interface acl entry key"; + } + + // no config container since the enclosing container is + // read-only + + container state { + + config false; + + description + "Operational state data for per-interface ACL entries"; + + uses interface-acl-entries-config; + uses interface-acl-entries-state; + } + } + } + } + + grouping interface-ingress-acl-config { + description + "Configuration data for per-interface ingress ACLs"; + + leaf set-name { + type leafref { + path "../../../../../../acl-sets/acl-set/config/name"; + } + description + "Reference to the ACL set name applied on ingress"; + } + + leaf type { + type leafref { + path "../../../../../../acl-sets/acl-set[name=current()/../set-name]" + + "/config/type"; + } + description + "Reference to the ACL set type applied on ingress"; + } + } + + grouping interface-ingress-acl-state { + description + "Operational state data for the per-interface ingress ACL"; + } + + grouping interface-ingress-acl-top { + description + "Top-level grouping for per-interface ingress ACL data"; + + container ingress-acl-sets { + description + "Enclosing container the list of ingress ACLs on the + interface"; + + list ingress-acl-set { + key "set-name type"; + description + "List of ingress ACLs on the interface"; + + leaf set-name { + type leafref { + path "../config/set-name"; + } + description + "Reference to set name list key"; + } + + leaf type { + type leafref { + path "../config/type"; + } + description + "Reference to type list key"; + } + + container config { + description + "Configuration data "; + + uses interface-ingress-acl-config; + } + + container state { + + config false; + + description + "Operational state data for interface ingress ACLs"; + + uses interface-ingress-acl-config; + uses interface-ingress-acl-state; + } + + uses interface-acl-entries-top; + } + } + } + + grouping interface-egress-acl-config { + description + "Configuration data for per-interface egress ACLs"; + + leaf set-name { + type leafref { + path "../../../../../../acl-sets/acl-set/config/name"; + } + description + "Reference to the ACL set name applied on egress"; + } + + leaf type { + type leafref { + path "../../../../../../acl-sets/acl-set[name=current()/../set-name]" + + "/config/type"; + } + description + "Reference to the ACL set type applied on egress."; + } + } + + grouping interface-egress-acl-state { + description + "Operational state data for the per-interface egress ACL"; + } + + grouping interface-egress-acl-top { + description + "Top-level grouping for per-interface egress ACL data"; + + container egress-acl-sets { + description + "Enclosing container the list of egress ACLs on the + interface"; + + list egress-acl-set { + key "set-name type"; + description + "List of egress ACLs on the interface"; + + leaf set-name { + type leafref { + path "../config/set-name"; + } + description + "Reference to set name list key"; + } + + leaf type { + type leafref { + path "../config/type"; + } + description + "Reference to type list key"; + } + + container config { + description + "Configuration data "; + + uses interface-egress-acl-config; + } + + container state { + + config false; + + description + "Operational state data for interface egress ACLs"; + + uses interface-egress-acl-config; + uses interface-egress-acl-state; + } + + uses interface-acl-entries-top; + } + } + } + + grouping acl-interfaces-config { + description + "Configuration data for interface references"; + + leaf id { + type oc-if:interface-id; + description + "User-defined identifier for the interface -- a common + convention could be '.'"; + } + } + + grouping acl-interfaces-state { + description + "Operational state data for interface references"; + } + + grouping acl-interfaces-top { + description + "Top-level grouping for interface-specific ACL data"; + + container interfaces { + description + "Enclosing container for the list of interfaces on which + ACLs are set"; + + list interface { + key "id"; + description + "List of interfaces on which ACLs are set"; + + leaf id { + type leafref { + path "../config/id"; + } + description + "Reference to the interface id list key"; + } + + container config { + description + "Configuration for ACL per-interface data"; + + uses acl-interfaces-config; + } + + container state { + + config false; + + description + "Operational state for ACL per-interface data"; + + uses acl-interfaces-config; + uses acl-interfaces-state; + } + + uses oc-if:interface-ref; + uses interface-ingress-acl-top; + uses interface-egress-acl-top; + } + } + } + + grouping acl-config { + description + "Global configuration data for ACLs"; + } + + grouping acl-state { + description + "Global operational state data for ACLs"; + + leaf counter-capability { + type identityref { + base ACL_COUNTER_CAPABILITY; + } + description + "System reported indication of how ACL counters are reported + by the target"; + } + } + grouping acl-top { + description + "Top level grouping for ACL data and structure"; + + container acl { + description + "Top level enclosing container for ACL model config + and operational state data"; + + container config { + description + "Global config data for ACLs"; + uses acl-config; + } + + container state { + + config false; + + description + "Global operational state data for ACLs"; + + uses acl-config; + uses acl-state; + } + + uses acl-set-top; + uses acl-interfaces-top; + } + } + + // data definition statements + uses acl-top; + + // augment statements + + +} diff --git a/models/yang/openconfig-if-ethernet.yang b/models/yang/openconfig-if-ethernet.yang new file mode 100644 index 0000000000..e917bba4d7 --- /dev/null +++ b/models/yang/openconfig-if-ethernet.yang @@ -0,0 +1,438 @@ +module openconfig-if-ethernet { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces/ethernet"; + + prefix "oc-eth"; + + // import some basic types + import openconfig-interfaces { prefix oc-if; } + import iana-if-type { prefix ift; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Model for managing Ethernet interfaces -- augments the OpenConfig + model for interface configuration and state."; + + oc-ext:openconfig-version "2.6.2"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "2.6.2"; + } + + revision "2018-09-04" { + description + "Remove in-crc-align-errors as it is a duplicate of + in-crc-errors"; + reference "2.6.1"; + } + + revision "2018-08-28" { + description + "Add Ethernet counter in-block-errors"; + reference "2.6.0"; + } + + revision "2018-07-02" { + description + "Add new ethernet counters of in-undersize-frames, + in-crc-align-errors and the distribution container"; + reference "2.5.0"; + } + + revision "2018-04-10" { + description + "Add identities for 2.5 and 5 Gbps."; + reference "2.4.0"; + } + + revision "2018-01-05" { + description + "Add logical loopback to interface."; + reference "2.3.0"; + } + + revision "2017-12-21" { + description + "Added IPv6 router advertisement configuration."; + reference "2.1.0"; + } + + revision "2017-07-14" { + description + "Added Ethernet/IP state data; Add dhcp-client; + migrate to OpenConfig types modules; Removed or + renamed opstate values"; + reference "2.0.0"; + } + + revision "2016-12-22" { + description + "Fixes to Ethernet interfaces model"; + reference "1.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + identity ETHERNET_SPEED { + description "base type to specify available Ethernet link + speeds"; + } + + identity SPEED_10MB { + base ETHERNET_SPEED; + description "10 Mbps Ethernet"; + } + + identity SPEED_100MB { + base ETHERNET_SPEED; + description "100 Mbps Ethernet"; + } + + identity SPEED_1GB { + base ETHERNET_SPEED; + description "1 Gbps Ethernet"; + } + + identity SPEED_2500MB { + base ETHERNET_SPEED; + description "2.5 Gbps Ethernet"; + } + + identity SPEED_5GB { + base ETHERNET_SPEED; + description "5 Gbps Ethernet"; + } + + identity SPEED_10GB { + base ETHERNET_SPEED; + description "10 Gbps Ethernet"; + } + + identity SPEED_25GB { + base ETHERNET_SPEED; + description "25 Gbps Ethernet"; + } + + identity SPEED_40GB { + base ETHERNET_SPEED; + description "40 Gbps Ethernet"; + } + + identity SPEED_50GB { + base ETHERNET_SPEED; + description "50 Gbps Ethernet"; + } + + identity SPEED_100GB { + base ETHERNET_SPEED; + description "100 Gbps Ethernet"; + } + + identity SPEED_UNKNOWN { + base ETHERNET_SPEED; + description + "Interface speed is unknown. Systems may report + speed UNKNOWN when an interface is down or unpopuplated (e.g., + pluggable not present)."; + } + + // typedef statements + + + // grouping statements + + grouping ethernet-interface-config { + description "Configuration items for Ethernet interfaces"; + + leaf mac-address { + type oc-yang:mac-address; + description + "Assigns a MAC address to the Ethernet interface. If not + specified, the corresponding operational state leaf is + expected to show the system-assigned MAC address."; + } + + leaf auto-negotiate { + type boolean; + default true; + description + "Set to TRUE to request the interface to auto-negotiate + transmission parameters with its peer interface. When + set to FALSE, the transmission parameters are specified + manually."; + reference + "IEEE 802.3-2012 auto-negotiation transmission parameters"; + } + + leaf duplex-mode { + type enumeration { + enum FULL { + description "Full duplex mode"; + } + enum HALF { + description "Half duplex mode"; + } + } + description + "When auto-negotiate is TRUE, this optionally sets the + duplex mode that will be advertised to the peer. If + unspecified, the interface should negotiate the duplex mode + directly (typically full-duplex). When auto-negotiate is + FALSE, this sets the duplex mode on the interface directly."; + } + + leaf port-speed { + type identityref { + base ETHERNET_SPEED; + } + description + "When auto-negotiate is TRUE, this optionally sets the + port-speed mode that will be advertised to the peer for + negotiation. If unspecified, it is expected that the + interface will select the highest speed available based on + negotiation. When auto-negotiate is set to FALSE, sets the + link speed to a fixed value -- supported values are defined + by ETHERNET_SPEED identities"; + } + + leaf enable-flow-control { + type boolean; + default false; + description + "Enable or disable flow control for this interface. + Ethernet flow control is a mechanism by which a receiver + may send PAUSE frames to a sender to stop transmission for + a specified time. + + This setting should override auto-negotiated flow control + settings. If left unspecified, and auto-negotiate is TRUE, + flow control mode is negotiated with the peer interface."; + reference + "IEEE 802.3x"; + } + } + + grouping ethernet-interface-state-counters { + description + "Ethernet-specific counters and statistics"; + + // ingress counters + + leaf in-mac-control-frames { + type oc-yang:counter64; + description + "MAC layer control frames received on the interface"; + } + + leaf in-mac-pause-frames { + type oc-yang:counter64; + description + "MAC layer PAUSE frames received on the interface"; + } + + leaf in-oversize-frames { + type oc-yang:counter64; + description + "The total number of frames received that were + longer than 1518 octets (excluding framing bits, + but including FCS octets) and were otherwise + well formed."; + } + + leaf in-undersize-frames { + type oc-yang:counter64; + description + "The total number of frames received that were + less than 64 octets long (excluding framing bits, + but including FCS octets) and were otherwise well + formed."; + reference + "RFC 2819: Remote Network Monitoring MIB - + etherStatsUndersizePkts"; + } + + leaf in-jabber-frames { + type oc-yang:counter64; + description + "Number of jabber frames received on the + interface. Jabber frames are typically defined as oversize + frames which also have a bad CRC. Implementations may use + slightly different definitions of what constitutes a jabber + frame. Often indicative of a NIC hardware problem."; + } + + leaf in-fragment-frames { + type oc-yang:counter64; + description + "The total number of frames received that were less than + 64 octets in length (excluding framing bits but including + FCS octets) and had either a bad Frame Check Sequence + (FCS) with an integral number of octets (FCS Error) or a + bad FCS with a non-integral number of octets (Alignment + Error)."; + } + + leaf in-8021q-frames { + type oc-yang:counter64; + description + "Number of 802.1q tagged frames received on the interface"; + } + + leaf in-crc-errors { + type oc-yang:counter64; + description + "The total number of frames received that + had a length (excluding framing bits, but + including FCS octets) of between 64 and 1518 + octets, inclusive, but had either a bad + Frame Check Sequence (FCS) with an integral + number of octets (FCS Error) or a bad FCS with + a non-integral number of octets (Alignment Error)"; + reference + "RFC 2819: Remote Network Monitoring MIB - + etherStatsCRCAlignErrors"; + } + + leaf in-block-errors { + type oc-yang:counter64; + description + "The number of received errored blocks. Error detection codes + are capable of detecting whether one or more errors have + occurred in a given sequence of bits – the block. It is + normally not possible to determine the exact number of errored + bits within the block"; + } + + // egress counters + + leaf out-mac-control-frames { + type oc-yang:counter64; + description + "MAC layer control frames sent on the interface"; + } + + leaf out-mac-pause-frames { + type oc-yang:counter64; + description + "MAC layer PAUSE frames sent on the interface"; + } + + leaf out-8021q-frames { + type oc-yang:counter64; + description + "Number of 802.1q tagged frames sent on the interface"; + } + } + + grouping ethernet-interface-state { + description + "Grouping for defining Ethernet-specific operational state"; + + leaf hw-mac-address { + type oc-yang:mac-address; + description + "Represenets the 'burned-in', or system-assigned, MAC + address for the Ethernet interface."; + } + + leaf negotiated-duplex-mode { + type enumeration { + enum FULL { + description "Full duplex mode"; + } + enum HALF { + description "Half duplex mode"; + } + } + description + "When auto-negotiate is set to TRUE, and the interface has + completed auto-negotiation with the remote peer, this value + shows the duplex mode that has been negotiated."; + } + + leaf negotiated-port-speed { + type identityref { + base ETHERNET_SPEED; + } + description + "When auto-negotiate is set to TRUE, and the interface has + completed auto-negotiation with the remote peer, this value + shows the interface speed that has been negotiated."; + } + + container counters { + description "Ethernet interface counters"; + + uses ethernet-interface-state-counters; + + } + + } + + // data definition statements + + grouping ethernet-top { + description "top-level Ethernet config and state containers"; + + container ethernet { + description + "Top-level container for ethernet configuration + and state"; + + container config { + description "Configuration data for ethernet interfaces"; + + uses ethernet-interface-config; + + } + + container state { + + config false; + description "State variables for Ethernet interfaces"; + + uses ethernet-interface-config; + uses ethernet-interface-state; + + } + + } + } + + // augment statements + + augment "/oc-if:interfaces/oc-if:interface" { + description "Adds addtional Ethernet-specific configuration to + interfaces model"; + + uses ethernet-top { + when "oc-if:state/oc-if:type = 'ift:ethernetCsmacd'" { + description "Additional interface configuration parameters when + the interface type is Ethernet"; + } + } + } + + // rpc statements + + // notification statements + +} diff --git a/models/yang/openconfig-if-ip.yang b/models/yang/openconfig-if-ip.yang new file mode 100644 index 0000000000..df89662f83 --- /dev/null +++ b/models/yang/openconfig-if-ip.yang @@ -0,0 +1,1322 @@ +module openconfig-if-ip { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces/ip"; + + prefix "oc-ip"; + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-vlan { prefix oc-vlan; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "This model defines data for managing configuration and + operational state on IP (IPv4 and IPv6) interfaces. + + This model reuses data items defined in the IETF YANG model for + interfaces described by RFC 7277 with an alternate structure + (particularly for operational state data) and with + additional configuration items. + + Portions of this code were derived from IETF RFC 7277. + Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "2.3.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "2.3.1"; + } + + revision "2018-01-05" { + description + "Add logical loopback to interface."; + reference "2.3.0"; + } + + revision "2017-12-21" { + description + "Added IPv6 router advertisement configuration."; + reference "2.1.0"; + } + + revision "2017-07-14" { + description + "Added Ethernet/IP state data; Add dhcp-client; + migrate to OpenConfig types modules; Removed or + renamed opstate values"; + reference "2.0.0"; + } + + revision "2017-04-03"{ + description + "Update copyright notice."; + reference "1.1.1"; + } + + revision "2016-12-22" { + description + "Fixes to Ethernet interfaces model"; + reference "1.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedef statements + + typedef ip-address-origin { + type enumeration { + enum OTHER { + description + "None of the following."; + } + enum STATIC { + description + "Indicates that the address has been statically + configured - for example, using NETCONF or a Command Line + Interface."; + } + enum DHCP { + description + "Indicates an address that has been assigned to this + system by a DHCP server."; + } + enum LINK_LAYER { + description + "Indicates an address created by IPv6 stateless + autoconfiguration that embeds a link-layer address in its + interface identifier."; + } + enum RANDOM { + description + "Indicates an address chosen by the system at + random, e.g., an IPv4 address within 169.254/16, an + RFC 4941 temporary address, or an RFC 7217 semantically + opaque address."; + reference + "RFC 4941: Privacy Extensions for Stateless Address + Autoconfiguration in IPv6 + RFC 7217: A Method for Generating Semantically Opaque + Interface Identifiers with IPv6 Stateless + Address Autoconfiguration (SLAAC)"; + } + } + description + "The origin of an address."; + } + + typedef neighbor-origin { + type enumeration { + enum OTHER { + description + "None of the following."; + } + enum STATIC { + description + "Indicates that the mapping has been statically + configured - for example, using NETCONF or a Command Line + Interface."; + } + enum DYNAMIC { + description + "Indicates that the mapping has been dynamically resolved + using, e.g., IPv4 ARP or the IPv6 Neighbor Discovery + protocol."; + } + } + description + "The origin of a neighbor entry."; + } + + // grouping statements + + grouping ip-common-global-config { + description + "Shared configuration data for IPv4 or IPv6 assigned + globally on an interface."; + + leaf dhcp-client { + type boolean; + default false; + description + "Enables a DHCP client on the interface in order to request + an address"; + } + } + + grouping ip-common-counters-state { + description + "Operational state for IP traffic statistics for IPv4 and + IPv6"; + + container counters { + description + "Packet and byte counters for IP transmission and + reception for the address family."; + + + leaf in-pkts { + type oc-yang:counter64; + description + "The total number of IP packets received for the specified + address family, including those received in error"; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf in-octets { + type oc-yang:counter64; + description + "The total number of octets received in input IP packets + for the specified address family, including those received + in error."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf in-error-pkts { + // TODO: this counter combines several error conditions -- + // could consider breaking them out to separate leaf nodes + type oc-yang:counter64; + description + "Number of IP packets discarded due to errors for the + specified address family, including errors in the IP + header, no route found to the IP destination, invalid + address, unknown protocol, etc."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf in-forwarded-pkts { + type oc-yang:counter64; + description + "The number of input packets for which the device was not + their final IP destination and for which the device + attempted to find a route to forward them to that final + destination."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf in-forwarded-octets { + type oc-yang:counter64; + description + "The number of octets received in input IP packets + for the specified address family for which the device was + not their final IP destination and for which the + device attempted to find a route to forward them to that + final destination."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf in-discarded-pkts { + type oc-yang:counter64; + description + "The number of input IP packets for the + specified address family, for which no problems were + encountered to prevent their continued processing, but + were discarded (e.g., for lack of buffer space)."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf out-pkts { + type oc-yang:counter64; + description + "The total number of IP packets for the + specified address family that the device supplied + to the lower layers for transmission. This includes + packets generated locally and those forwarded by the + device."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf out-octets { + type oc-yang:counter64; + description + "The total number of octets in IP packets for the + specified address family that the device + supplied to the lower layers for transmission. This + includes packets generated locally and those forwarded by + the device."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf out-error-pkts { + // TODO: this counter combines several error conditions -- + // could consider breaking them out to separate leaf nodes + type oc-yang:counter64; + description + "Number of IP packets for the specified address family + locally generated and discarded due to errors, including + no route found to the IP destination."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf out-forwarded-pkts { + type oc-yang:counter64; + description + "The number of packets for which this entity was not their + final IP destination and for which it was successful in + finding a path to their final destination."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf out-forwarded-octets { + type oc-yang:counter64; + description + "The number of octets in packets for which this entity was + not their final IP destination and for which it was + successful in finding a path to their final destination."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + + leaf out-discarded-pkts { + type oc-yang:counter64; + description + "The number of output IP packets for the + specified address family for which no problem was + encountered to prevent their transmission to their + destination, but were discarded (e.g., for lack of + buffer space)."; + reference + "RFC 4293 - Management Information Base for the + Internet Protocol (IP)"; + } + } + + } + + + + grouping ipv4-global-config { + description + "Configuration data for IPv4 interfaces across + all addresses assigned to the interface"; + + leaf enabled { + type boolean; + default true; + description + "Controls whether IPv4 is enabled or disabled on this + interface. When IPv4 is enabled, this interface is + connected to an IPv4 stack, and the interface can send + and receive IPv4 packets."; + } + + leaf mtu { + type uint16 { + range "68..max"; + } + units octets; + description + "The size, in octets, of the largest IPv4 packet that the + interface will send and receive. + + The server may restrict the allowed values for this leaf, + depending on the interface's type. + + If this leaf is not configured, the operationally used MTU + depends on the interface's type."; + reference + "RFC 791: Internet Protocol"; + } + + uses ip-common-global-config; + + + } + + grouping ipv4-address-config { + + description + "Per IPv4 adresss configuration data for the + interface."; + + leaf ip { + type oc-inet:ipv4-address; + description + "The IPv4 address on the interface."; + } + + leaf prefix-length { + type uint8 { + range "0..32"; + } + description + "The length of the subnet prefix."; + } + } + + grouping ipv4-neighbor-config { + description + "Per IPv4 neighbor configuration data. Neighbor + entries are analagous to static ARP entries, i.e., they + create a correspondence between IP and link-layer addresses"; + + leaf ip { + type oc-inet:ipv4-address; + description + "The IPv4 address of the neighbor node."; + } + leaf link-layer-address { + type oc-yang:phys-address; + mandatory true; + description + "The link-layer address of the neighbor node."; + } + } + + grouping ipv4-address-state { + description + "State variables for IPv4 addresses on the interface"; + + leaf origin { + type ip-address-origin; + description + "The origin of this address, e.g., statically configured, + assigned by DHCP, etc.."; + } + } + + grouping ipv4-neighbor-state { + description + "State variables for IPv4 neighbor entries on the interface."; + + leaf origin { + type neighbor-origin; + description + "The origin of this neighbor entry, static or dynamic."; + } + } + + grouping ipv6-global-config { + description + "Configuration data at the global level for each + IPv6 interface"; + + leaf enabled { + type boolean; + default true; + description + "Controls whether IPv6 is enabled or disabled on this + interface. When IPv6 is enabled, this interface is + connected to an IPv6 stack, and the interface can send + and receive IPv6 packets."; + } + + leaf mtu { + type uint32 { + range "1280..max"; + } + units octets; + description + "The size, in octets, of the largest IPv6 packet that the + interface will send and receive. + + The server may restrict the allowed values for this leaf, + depending on the interface's type. + + If this leaf is not configured, the operationally used MTU + depends on the interface's type."; + reference + "RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + Section 5"; + } + + leaf dup-addr-detect-transmits { + type uint32; + default 1; + description + "The number of consecutive Neighbor Solicitation messages + sent while performing Duplicate Address Detection on a + tentative address. A value of zero indicates that + Duplicate Address Detection is not performed on + tentative addresses. A value of one indicates a single + transmission with no follow-up retransmissions."; + reference + "RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + + uses ip-common-global-config; + } + + grouping ipv6-address-config { + description "Per-address configuration data for IPv6 interfaces"; + + leaf ip { + type oc-inet:ipv6-address; + description + "The IPv6 address on the interface."; + } + + leaf prefix-length { + type uint8 { + range "0..128"; + } + mandatory true; + description + "The length of the subnet prefix."; + } + } + + grouping ipv6-address-state { + description + "Per-address operational state data for IPv6 interfaces"; + + leaf origin { + type ip-address-origin; + description + "The origin of this address, e.g., static, dhcp, etc."; + } + + leaf status { + type enumeration { + enum PREFERRED { + description + "This is a valid address that can appear as the + destination or source address of a packet."; + } + enum DEPRECATED { + description + "This is a valid but deprecated address that should + no longer be used as a source address in new + communications, but packets addressed to such an + address are processed as expected."; + } + enum INVALID { + description + "This isn't a valid address, and it shouldn't appear + as the destination or source address of a packet."; + } + enum INACCESSIBLE { + description + "The address is not accessible because the interface + to which this address is assigned is not + operational."; + } + enum UNKNOWN { + description + "The status cannot be determined for some reason."; + } + enum TENTATIVE { + description + "The uniqueness of the address on the link is being + verified. Addresses in this state should not be + used for general communication and should only be + used to determine the uniqueness of the address."; + } + enum DUPLICATE { + description + "The address has been determined to be non-unique on + the link and so must not be used."; + } + enum OPTIMISTIC { + description + "The address is available for use, subject to + restrictions, while its uniqueness on a link is + being verified."; + } + } + description + "The status of an address. Most of the states correspond + to states from the IPv6 Stateless Address + Autoconfiguration protocol."; + reference + "RFC 4293: Management Information Base for the + Internet Protocol (IP) + - IpAddressStatusTC + RFC 4862: IPv6 Stateless Address Autoconfiguration"; + } + } + + grouping ipv6-neighbor-config { + description + "Per-neighbor configuration data for IPv6 interfaces"; + + leaf ip { + type oc-inet:ipv6-address; + description + "The IPv6 address of the neighbor node."; + } + + leaf link-layer-address { + type oc-yang:phys-address; + mandatory true; + description + "The link-layer address of the neighbor node."; + } + } + + grouping ipv6-neighbor-state { + description "Per-neighbor state variables for IPv6 interfaces"; + + leaf origin { + type neighbor-origin; + description + "The origin of this neighbor entry."; + } + leaf is-router { + type empty; + description + "Indicates that the neighbor node acts as a router."; + } + leaf neighbor-state { + type enumeration { + enum INCOMPLETE { + description + "Address resolution is in progress, and the link-layer + address of the neighbor has not yet been + determined."; + } + enum REACHABLE { + description + "Roughly speaking, the neighbor is known to have been + reachable recently (within tens of seconds ago)."; + } + enum STALE { + description + "The neighbor is no longer known to be reachable, but + until traffic is sent to the neighbor no attempt + should be made to verify its reachability."; + } + enum DELAY { + description + "The neighbor is no longer known to be reachable, and + traffic has recently been sent to the neighbor. + Rather than probe the neighbor immediately, however, + delay sending probes for a short while in order to + give upper-layer protocols a chance to provide + reachability confirmation."; + } + enum PROBE { + description + "The neighbor is no longer known to be reachable, and + unicast Neighbor Solicitation probes are being sent + to verify reachability."; + } + } + description + "The Neighbor Unreachability Detection state of this + entry."; + reference + "RFC 4861: Neighbor Discovery for IP version 6 (IPv6) + Section 7.3.2"; + } + } + + grouping ip-vrrp-ipv6-config { + description + "IPv6-specific configuration data for VRRP on IPv6 + interfaces"; + + leaf virtual-link-local { + type oc-inet:ip-address; + description + "For VRRP on IPv6 interfaces, sets the virtual link local + address"; + } + } + + grouping ip-vrrp-ipv6-state { + description + "IPv6-specific operational state for VRRP on IPv6 interfaces"; + + uses ip-vrrp-ipv6-config; + } + + grouping ip-vrrp-tracking-config { + description + "Configuration data for tracking interfaces + in a VRRP group"; + + leaf-list track-interface { + type leafref { + path "/oc-if:interfaces/oc-if:interface/oc-if:name"; + } + // TODO: we may need to add some restriction to ethernet + // or IP interfaces. + description + "Sets a list of one or more interfaces that should + be tracked for up/down events to dynamically change the + priority state of the VRRP group, and potentially + change the mastership if the tracked interface going + down lowers the priority sufficiently. Any of the tracked + interfaces going down will cause the priority to be lowered. + Some implementations may only support a single + tracked interface."; + } + + leaf priority-decrement { + type uint8 { + range 0..254; + } + default 0; + description "Set the value to subtract from priority when + the tracked interface goes down"; + } + } + + grouping ip-vrrp-tracking-state { + description + "Operational state data for tracking interfaces in a VRRP + group"; + } + + grouping ip-vrrp-tracking-top { + description + "Top-level grouping for VRRP interface tracking"; + + container interface-tracking { + description + "Top-level container for VRRP interface tracking"; + + container config { + description + "Configuration data for VRRP interface tracking"; + + uses ip-vrrp-tracking-config; + } + + container state { + + config false; + + description + "Operational state data for VRRP interface tracking"; + + uses ip-vrrp-tracking-config; + uses ip-vrrp-tracking-state; + } + } + } + + grouping ip-vrrp-config { + description + "Configuration data for VRRP on IP interfaces"; + + leaf virtual-router-id { + type uint8 { + range 1..255; + } + description + "Set the virtual router id for use by the VRRP group. This + usually also determines the virtual MAC address that is + generated for the VRRP group"; + } + + leaf-list virtual-address { + type oc-inet:ip-address; + description + "Configure one or more virtual addresses for the + VRRP group"; + } + + leaf priority { + type uint8 { + range 1..254; + } + default 100; + description + "Specifies the sending VRRP interface's priority + for the virtual router. Higher values equal higher + priority"; + } + + leaf preempt { + type boolean; + default true; + description + "When set to true, enables preemption by a higher + priority backup router of a lower priority master router"; + } + + leaf preempt-delay { + type uint16 { + range 0..3600; + } + default 0; + description + "Set the delay the higher priority router waits + before preempting"; + } + + leaf accept-mode { + type boolean; + // TODO: should we adopt the RFC default given the common + // operational practice of setting to true? + default false; + description + "Configure whether packets destined for + virtual addresses are accepted even when the virtual + address is not owned by the router interface"; + } + + leaf advertisement-interval { + type uint16 { + range 1..4095; + } + // TODO this range is theoretical -- needs to be validated + // against major implementations. + units "centiseconds"; + default 100; + description + "Sets the interval between successive VRRP + advertisements -- RFC 5798 defines this as a 12-bit + value expressed as 0.1 seconds, with default 100, i.e., + 1 second. Several implementation express this in units of + seconds"; + } + } + + grouping ip-vrrp-state { + description + "Operational state data for VRRP on IP interfaces"; + + leaf current-priority { + type uint8; + description "Operational value of the priority for the + interface in the VRRP group"; + } + } + + grouping ip-vrrp-top { + description + "Top-level grouping for Virtual Router Redundancy Protocol"; + + container vrrp { + description + "Enclosing container for VRRP groups handled by this + IP interface"; + + reference "RFC 5798 - Virtual Router Redundancy Protocol + (VRRP) Version 3 for IPv4 and IPv6"; + + list vrrp-group { + key "virtual-router-id"; + description + "List of VRRP groups, keyed by virtual router id"; + + leaf virtual-router-id { + type leafref { + path "../config/virtual-router-id"; + } + description + "References the configured virtual router id for this + VRRP group"; + } + + container config { + description + "Configuration data for the VRRP group"; + + uses ip-vrrp-config; + } + + container state { + + config false; + + description + "Operational state data for the VRRP group"; + + uses ip-vrrp-config; + uses ip-vrrp-state; + } + + uses ip-vrrp-tracking-top; + } + } + } + + grouping ipv6-ra-config { + description + "Configuration parameters for IPv6 router advertisements."; + + leaf interval { + type uint32; + units seconds; + description + "The interval between periodic router advertisement neighbor + discovery messages sent on this interface expressed in + seconds."; + } + + leaf lifetime { + type uint32; + units seconds; + description + "The lifetime advertised in the router advertisement neighbor + discovery message on this interface."; + } + + leaf suppress { + type boolean; + default false; + description + "When set to true, router advertisement neighbor discovery + messages are not transmitted on this interface."; + } + } + + grouping ipv4-proxy-arp-config { + description + "Configuration parameters for IPv4 proxy ARP"; + + leaf mode { + type enumeration { + enum DISABLE { + description + "The system should not respond to ARP requests that + do not specify an IP address configured on the local + subinterface as the target address."; + } + enum REMOTE_ONLY { + description + "The system responds to ARP requests only when the + sender and target IP addresses are in different + subnets."; + } + enum ALL { + description + "The system responds to ARP requests where the sender + and target IP addresses are in different subnets, as well + as those where they are in the same subnet."; + } + } + default "DISABLE"; + description + "When set to a value other than DISABLE, the local system should + respond to ARP requests that are for target addresses other than + those that are configured on the local subinterface using its own + MAC address as the target hardware address. If the REMOTE_ONLY + value is specified, replies are only sent when the target address + falls outside the locally configured subnets on the interface, + whereas with the ALL value, all requests, regardless of their + target address are replied to."; + reference "RFC1027: Using ARP to Implement Transparent Subnet Gateways"; + } + } + + grouping ipv4-top { + description "Top-level configuration and state for IPv4 + interfaces"; + + container ipv4 { + description + "Parameters for the IPv4 address family."; + + container addresses { + description + "Enclosing container for address list"; + + list address { + key "ip"; + description + "The list of configured IPv4 addresses on the interface."; + + leaf ip { + type leafref { + path "../config/ip"; + } + description "References the configured IP address"; + } + + container config { + description "Configuration data for each configured IPv4 + address on the interface"; + + uses ipv4-address-config; + + } + + container state { + + config false; + description "Operational state data for each IPv4 address + configured on the interface"; + + uses ipv4-address-config; + uses ipv4-address-state; + } + + } + } + + container proxy-arp { + description + "Configuration and operational state parameters + relating to proxy ARP. This functionality allows a + system to respond to ARP requests that are not + explicitly destined to the local system."; + + container config { + description + "Configuration parameters for proxy ARP"; + uses ipv4-proxy-arp-config; + } + + container state { + config false; + description + "Operational state parameters for proxy ARP"; + uses ipv4-proxy-arp-config; + } + } + + container neighbors { + description + "Enclosing container for neighbor list"; + + list neighbor { + key "ip"; + description + "A list of mappings from IPv4 addresses to + link-layer addresses. + + Entries in this list are used as static entries in the + ARP Cache."; + reference + "RFC 826: An Ethernet Address Resolution Protocol"; + + leaf ip { + type leafref { + path "../config/ip"; + } + description "References the configured IP address"; + } + + container config { + description "Configuration data for each configured IPv4 + address on the interface"; + + uses ipv4-neighbor-config; + + } + + container state { + + config false; + description "Operational state data for each IPv4 address + configured on the interface"; + + uses ipv4-neighbor-config; + uses ipv4-neighbor-state; + } + } + } + + uses oc-if:sub-unnumbered-top; + + container config { + description + "Top-level IPv4 configuration data for the interface"; + + uses ipv4-global-config; + } + + container state { + + config false; + description + "Top level IPv4 operational state data"; + + uses ipv4-global-config; + uses ip-common-counters-state; + } + } + } + + grouping ipv6-top { + description + "Top-level configuration and state for IPv6 interfaces"; + + container ipv6 { + description + "Parameters for the IPv6 address family."; + + container addresses { + description + "Enclosing container for address list"; + + list address { + key "ip"; + description + "The list of configured IPv6 addresses on the interface."; + + leaf ip { + type leafref { + path "../config/ip"; + } + description "References the configured IP address"; + } + + container config { + description + "Configuration data for each IPv6 address on + the interface"; + + uses ipv6-address-config; + + } + + container state { + + config false; + description + "State data for each IPv6 address on the + interface"; + + uses ipv6-address-config; + uses ipv6-address-state; + } + } + } + + container router-advertisement { + description + "Configuration and operational state parameters relating to + router advertisements."; + + container config { + description + "Configuration parameters relating to router advertisements + for IPv6."; + uses ipv6-ra-config; + } + + container state { + config false; + description + "Operational state parameters relating to router + advertisements for IPv6."; + uses ipv6-ra-config; + } + } + + container neighbors { + description + "Enclosing container for list of IPv6 neighbors"; + + list neighbor { + key "ip"; + description + "List of IPv6 neighbors"; + + leaf ip { + type leafref { + path "../config/ip"; + } + description + "References the configured IP neighbor address"; + } + + container config { + description "Configuration data for each IPv6 address on + the interface"; + + uses ipv6-neighbor-config; + + } + + container state { + + config false; + description "State data for each IPv6 address on the + interface"; + + uses ipv6-neighbor-config; + uses ipv6-neighbor-state; + } + } + } + uses oc-if:sub-unnumbered-top; + + container config { + description "Top-level config data for the IPv6 interface"; + + uses ipv6-global-config; + } + + container state { + config false; + description + "Top-level operational state data for the IPv6 interface"; + + uses ipv6-global-config; + uses ip-common-counters-state; + + } + } + } + + // augment statements + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface" { + description + "IPv4 address family configuration for + interfaces"; + + uses ipv4-top; + + } + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface" { + description + "IPv6 address family configuration for + interfaces"; + + uses ipv6-top; + + } + + // VRRP for IPv4 interfaces + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface/oc-ip:ipv4/oc-ip:addresses/oc-ip:address" { + + description + "Additional IP addr family configuration for + interfaces"; + + uses ip-vrrp-top; + + } + + // VRRP for IPv6 interfaces + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface/oc-ip:ipv6/oc-ip:addresses/oc-ip:address" { + description + "Additional IP addr family configuration for + interfaces"; + + uses ip-vrrp-top; + + } + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface/oc-ip:ipv6/oc-ip:addresses/oc-ip:address/" + + "vrrp/vrrp-group/config" { + description + "Additional VRRP data for IPv6 interfaces"; + + uses ip-vrrp-ipv6-config; + } + + augment "/oc-if:interfaces/oc-if:interface/oc-if:subinterfaces/" + + "oc-if:subinterface/oc-ip:ipv6/oc-ip:addresses/oc-ip:address/vrrp/" + + "vrrp-group/state" { + description + "Additional VRRP data for IPv6 interfaces"; + + uses ip-vrrp-ipv6-state; + } + + // Augments for for routed VLANs + + augment "/oc-if:interfaces/oc-if:interface/oc-vlan:routed-vlan" { + description + "IPv4 address family configuration for + interfaces"; + + uses ipv4-top; + } + + augment "/oc-if:interfaces/oc-if:interface/oc-vlan:routed-vlan" { + description + "IPv6 address family configuration for + interfaces"; + + uses ipv6-top; + } + + // VRRP for routed VLAN interfaces + + augment "/oc-if:interfaces/oc-if:interface/oc-vlan:routed-vlan/" + + "oc-ip:ipv4/oc-ip:addresses/oc-ip:address" { + description + "Additional IP addr family configuration for + interfaces"; + + uses ip-vrrp-top; + + } + + augment "/oc-if:interfaces/oc-if:interface/oc-vlan:routed-vlan/" + + "oc-ip:ipv6/oc-ip:addresses/oc-ip:address" { + description + "Additional IP addr family configuration for + interfaces"; + + uses ip-vrrp-top; + + } + + augment "/oc-if:interfaces/oc-if:interface/oc-vlan:routed-vlan/" + + "oc-ip:ipv6/oc-ip:addresses/oc-ip:address/vrrp/vrrp-group/config" { + description + "Additional VRRP data for IPv6 interfaces"; + + uses ip-vrrp-ipv6-config; + } + + + augment "/oc-if:interfaces/oc-if:interface/oc-vlan:routed-vlan/" + + "oc-ip:ipv6/oc-ip:addresses/oc-ip:address/vrrp/vrrp-group/state" { + description + "Additional VRRP data for IPv6 interfaces"; + + uses ip-vrrp-ipv6-state; + } + + // rpc statements + + // notification statements +} diff --git a/models/yang/openconfig-interfaces.yang b/models/yang/openconfig-interfaces.yang new file mode 100644 index 0000000000..f3e0feeace --- /dev/null +++ b/models/yang/openconfig-interfaces.yang @@ -0,0 +1,1067 @@ +module openconfig-interfaces { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/interfaces"; + + prefix "oc-if"; + + // import some basic types + import ietf-interfaces { prefix ietf-if; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-types { prefix oc-types; } + import openconfig-extensions { prefix oc-ext; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Model for managing network interfaces and subinterfaces. This + module also defines convenience types / groupings for other + models to create references to interfaces: + + base-interface-ref (type) - reference to a base interface + interface-ref (grouping) - container for reference to a + interface + subinterface + interface-ref-state (grouping) - container for read-only + (opstate) reference to interface + subinterface + + This model reuses data items defined in the IETF YANG model for + interfaces described by RFC 7223 with an alternate structure + (particularly for operational state data) and with + additional configuration items. + + Portions of this code were derived from IETF RFC 7223. + Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "2.4.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "2.4.1"; + } + + revision "2018-08-07" { + description + "Add leaf to indicate whether an interface is physical or + logical."; + reference "2.4.0"; + } + + revision "2018-07-02" { + description + "Add in-pkts and out-pkts in counters"; + reference "2.3.2"; + } + + revision "2018-04-24" { + description + "Clarified behavior of last-change state leaf"; + reference "2.3.1"; + } + + revision "2018-01-05" { + description + "Add logical loopback to interface."; + reference "2.3.0"; + } + + revision "2017-12-22" { + description + "Add IPv4 proxy ARP configuration."; + reference "2.2.0"; + } + + revision "2017-12-21" { + description + "Added IPv6 router advertisement configuration."; + reference "2.1.0"; + } + + revision "2017-07-14" { + description + "Added Ethernet/IP state data; Add dhcp-client; + migrate to OpenConfig types modules; Removed or + renamed opstate values"; + reference "2.0.0"; + } + + revision "2017-04-03" { + description + "Update copyright notice."; + reference "1.1.1"; + } + + revision "2016-12-22" { + description + "Fixes to Ethernet interfaces model"; + reference "1.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // typedef statements + + typedef base-interface-ref { + type leafref { + path "/oc-if:interfaces/oc-if:interface/oc-if:name"; + } + description + "Reusable type for by-name reference to a base interface. + This type may be used in cases where ability to reference + a subinterface is not required."; + } + + typedef interface-id { + type string; + description + "User-defined identifier for an interface, generally used to + name a interface reference. The id can be arbitrary but a + useful convention is to use a combination of base interface + name and subinterface index."; + } + + // grouping statements + + grouping interface-ref-common { + description + "Reference leafrefs to interface / subinterface"; + + leaf interface { + type leafref { + path "/oc-if:interfaces/oc-if:interface/oc-if:name"; + } + description + "Reference to a base interface. If a reference to a + subinterface is required, this leaf must be specified + to indicate the base interface."; + } + + leaf subinterface { + type leafref { + path "/oc-if:interfaces/" + + "oc-if:interface[oc-if:name=current()/../interface]/" + + "oc-if:subinterfaces/oc-if:subinterface/oc-if:index"; + } + description + "Reference to a subinterface -- this requires the base + interface to be specified using the interface leaf in + this container. If only a reference to a base interface + is requuired, this leaf should not be set."; + } + } + + grouping interface-ref-state-container { + description + "Reusable opstate w/container for a reference to an + interface or subinterface"; + + container state { + config false; + description + "Operational state for interface-ref"; + + uses interface-ref-common; + } + } + + grouping interface-ref { + description + "Reusable definition for a reference to an interface or + subinterface"; + + container interface-ref { + description + "Reference to an interface or subinterface"; + + container config { + description + "Configured reference to interface / subinterface"; + oc-ext:telemetry-on-change; + + uses interface-ref-common; + } + + uses interface-ref-state-container; + } + } + + grouping interface-ref-state { + description + "Reusable opstate w/container for a reference to an + interface or subinterface"; + + container interface-ref { + description + "Reference to an interface or subinterface"; + + uses interface-ref-state-container; + } + } + + grouping base-interface-ref-state { + description + "Reusable opstate w/container for a reference to a + base interface (no subinterface)."; + + container state { + config false; + description + "Operational state for base interface reference"; + + leaf interface { + type base-interface-ref; + description + "Reference to a base interface."; + } + } + } + + + grouping interface-common-config { + description + "Configuration data data nodes common to physical interfaces + and subinterfaces"; + + leaf description { + type string; + description + "A textual description of the interface. + + A server implementation MAY map this leaf to the ifAlias + MIB object. Such an implementation needs to use some + mechanism to handle the differences in size and characters + allowed between this leaf and ifAlias. The definition of + such a mechanism is outside the scope of this document. + + Since ifAlias is defined to be stored in non-volatile + storage, the MIB implementation MUST map ifAlias to the + value of 'description' in the persistently stored + datastore. + + Specifically, if the device supports ':startup', when + ifAlias is read the device MUST return the value of + 'description' in the 'startup' datastore, and when it is + written, it MUST be written to the 'running' and 'startup' + datastores. Note that it is up to the implementation to + + decide whether to modify this single leaf in 'startup' or + perform an implicit copy-config from 'running' to + 'startup'. + + If the device does not support ':startup', ifAlias MUST + be mapped to the 'description' leaf in the 'running' + datastore."; + reference + "RFC 2863: The Interfaces Group MIB - ifAlias"; + } + + leaf enabled { + type boolean; + default "true"; + description + "This leaf contains the configured, desired state of the + interface. + + Systems that implement the IF-MIB use the value of this + leaf in the 'running' datastore to set + IF-MIB.ifAdminStatus to 'up' or 'down' after an ifEntry + has been initialized, as described in RFC 2863. + + Changes in this leaf in the 'running' datastore are + reflected in ifAdminStatus, but if ifAdminStatus is + changed over SNMP, this leaf is not affected."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + } + + } + + grouping interface-phys-config { + description + "Configuration data for physical interfaces"; + + leaf name { + type string; + description + "The name of the interface. + + A device MAY restrict the allowed values for this leaf, + possibly depending on the type of the interface. + For system-controlled interfaces, this leaf is the + device-specific name of the interface. The 'config false' + list interfaces/interface[name]/state contains the currently + existing interfaces on the device. + + If a client tries to create configuration for a + system-controlled interface that is not present in the + corresponding state list, the server MAY reject + the request if the implementation does not support + pre-provisioning of interfaces or if the name refers to + an interface that can never exist in the system. A + NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case. + + The IETF model in RFC 7223 provides YANG features for the + following (i.e., pre-provisioning and arbitrary-names), + however they are omitted here: + + If the device supports pre-provisioning of interface + configuration, the 'pre-provisioning' feature is + advertised. + + If the device allows arbitrarily named user-controlled + interfaces, the 'arbitrary-names' feature is advertised. + + When a configured user-controlled interface is created by + the system, it is instantiated with the same name in the + /interfaces/interface[name]/state list."; + } + + leaf type { + type identityref { + base ietf-if:interface-type; + } + mandatory true; + description + "The type of the interface. + + When an interface entry is created, a server MAY + initialize the type leaf with a valid value, e.g., if it + is possible to derive the type from the name of the + interface. + + If a client tries to set the type of an interface to a + value that can never be used by the system, e.g., if the + type is not supported or if the type does not match the + name of the interface, the server MUST reject the request. + A NETCONF server MUST reply with an rpc-error with the + error-tag 'invalid-value' in this case."; + reference + "RFC 2863: The Interfaces Group MIB - ifType"; + } + + leaf mtu { + type uint16; + description + "Set the max transmission unit size in octets + for the physical interface. If this is not set, the mtu is + set to the operational default -- e.g., 1514 bytes on an + Ethernet interface."; + } + + leaf loopback-mode { + type boolean; + default false; + description + "When set to true, the interface is logically looped back, + such that packets that are forwarded via the interface + are received on the same interface."; + } + + uses interface-common-config; + } + + grouping interface-phys-holdtime-config { + description + "Configuration data for interface hold-time settings -- + applies to physical interfaces."; + + leaf up { + type uint32; + units milliseconds; + default 0; + description + "Dampens advertisement when the interface + transitions from down to up. A zero value means dampening + is turned off, i.e., immediate notification."; + } + + leaf down { + type uint32; + units milliseconds; + default 0; + description + "Dampens advertisement when the interface transitions from + up to down. A zero value means dampening is turned off, + i.e., immediate notification."; + } + } + + grouping interface-phys-holdtime-state { + description + "Operational state data for interface hold-time."; + } + + grouping interface-phys-holdtime-top { + description + "Top-level grouping for setting link transition + dampening on physical and other types of interfaces."; + + container hold-time { + description + "Top-level container for hold-time settings to enable + dampening advertisements of interface transitions."; + + container config { + description + "Configuration data for interface hold-time settings."; + oc-ext:telemetry-on-change; + + uses interface-phys-holdtime-config; + } + + container state { + + config false; + + description + "Operational state data for interface hold-time."; + + uses interface-phys-holdtime-config; + uses interface-phys-holdtime-state; + } + } + } + + grouping interface-common-state { + description + "Operational state data (in addition to intended configuration) + at the global level for this interface"; + + oc-ext:operational; + + leaf ifindex { + type uint32; + description + "System assigned number for each interface. Corresponds to + ifIndex object in SNMP Interface MIB"; + reference + "RFC 2863 - The Interfaces Group MIB"; + oc-ext:telemetry-on-change; + } + + leaf admin-status { + type enumeration { + enum UP { + description + "Ready to pass packets."; + } + enum DOWN { + description + "Not ready to pass packets and not in some test mode."; + } + enum TESTING { + //TODO: This is generally not supported as a configured + //admin state, though it's in the standard interfaces MIB. + //Consider removing it. + description + "In some test mode."; + } + } + //TODO:consider converting to an identity to have the + //flexibility to remove some values defined by RFC 7223 that + //are not used or not implemented consistently. + mandatory true; + description + "The desired state of the interface. In RFC 7223 this leaf + has the same read semantics as ifAdminStatus. Here, it + reflects the administrative state as set by enabling or + disabling the interface."; + reference + "RFC 2863: The Interfaces Group MIB - ifAdminStatus"; + oc-ext:telemetry-on-change; + } + + leaf oper-status { + type enumeration { + enum UP { + value 1; + description + "Ready to pass packets."; + } + enum DOWN { + value 2; + description + "The interface does not pass any packets."; + } + enum TESTING { + value 3; + description + "In some test mode. No operational packets can + be passed."; + } + enum UNKNOWN { + value 4; + description + "Status cannot be determined for some reason."; + } + enum DORMANT { + value 5; + description + "Waiting for some external event."; + } + enum NOT_PRESENT { + value 6; + description + "Some component (typically hardware) is missing."; + } + enum LOWER_LAYER_DOWN { + value 7; + description + "Down due to state of lower-layer interface(s)."; + } + } + //TODO:consider converting to an identity to have the + //flexibility to remove some values defined by RFC 7223 that + //are not used or not implemented consistently. + mandatory true; + description + "The current operational state of the interface. + + This leaf has the same semantics as ifOperStatus."; + reference + "RFC 2863: The Interfaces Group MIB - ifOperStatus"; + oc-ext:telemetry-on-change; + } + + leaf last-change { + type oc-types:timeticks64; + units nanoseconds; + description + "This timestamp indicates the absolute time of the last + state change of the interface (e.g., up-to-down transition). + This is different than the SNMP ifLastChange object in the + standard interface MIB in that it is not relative to the + system boot time (i.e,. sysUpTime). + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + oc-ext:telemetry-on-change; + } + + leaf logical { + type boolean; + description + "When set to true, the interface is a logical interface + which does not have an associated physical port or + channel on the system."; + oc-ext:telemetry-on-change; + } + } + + + grouping interface-counters-state { + description + "Operational state representing interface counters + and statistics."; + + //TODO: we may need to break this list of counters into those + //that would appear for physical vs. subinterface or logical + //interfaces. For now, just replicating the full stats + //grouping to both interface and subinterface. + + oc-ext:operational; + + container counters { + description + "A collection of interface-related statistics objects."; + + leaf in-octets { + type oc-yang:counter64; + description + "The total number of octets received on the interface, + including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInOctets"; + } + + leaf in-pkts { + type oc-yang:counter64; + description + "The total number of packets received on the interface, + including all unicast, multicast, broadcast and bad packets + etc."; + reference + "RFC 2819: Remote Network Monitoring Management Information + Base"; + } + + leaf in-unicast-pkts { + type oc-yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were not addressed to a + multicast or broadcast address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCInUcastPkts"; + } + + leaf in-broadcast-pkts { + type oc-yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a broadcast + address at this sub-layer. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInBroadcastPkts"; + } + + leaf in-multicast-pkts { + type oc-yang:counter64; + description + "The number of packets, delivered by this sub-layer to a + higher (sub-)layer, that were addressed to a multicast + address at this sub-layer. For a MAC-layer protocol, + this includes both Group and Functional addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCInMulticastPkts"; + } + + leaf in-discards { + type oc-yang:counter64; + description + "The number of inbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being deliverable to a higher-layer + protocol. One possible reason for discarding such a + packet could be to free up buffer space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + + + reference + "RFC 2863: The Interfaces Group MIB - ifInDiscards"; + } + + leaf in-errors { + type oc-yang:counter64; + description + "For packet-oriented interfaces, the number of inbound + packets that contained errors preventing them from being + deliverable to a higher-layer protocol. For character- + oriented or fixed-length interfaces, the number of + inbound transmission units that contained errors + preventing them from being deliverable to a higher-layer + protocol. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInErrors"; + } + + leaf in-unknown-protos { + type oc-yang:counter64; + description + "For packet-oriented interfaces, the number of packets + received via the interface that were discarded because + of an unknown or unsupported protocol. For + character-oriented or fixed-length interfaces that + support protocol multiplexing, the number of + transmission units received via the interface that were + discarded because of an unknown or unsupported protocol. + For any interface that does not support protocol + multiplexing, this counter is not present. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifInUnknownProtos"; + } + + leaf in-fcs-errors { + type oc-yang:counter64; + description + "Number of received packets which had errors in the + frame check sequence (FCS), i.e., framing errors. + + Discontinuities in the value of this counter can occur + when the device is re-initialization as indicated by the + value of 'last-clear'."; + } + + leaf out-octets { + type oc-yang:counter64; + description + "The total number of octets transmitted out of the + interface, including framing characters. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutOctets"; + } + + leaf out-pkts { + type oc-yang:counter64; + description + "The total number of packets transmitted out of the + interface, including all unicast, multicast, broadcast, + and bad packets etc."; + reference + "RFC 2819: Remote Network Monitoring Management Information + Base"; + } + + leaf out-unicast-pkts { + type oc-yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were not addressed + to a multicast or broadcast address at this sub-layer, + including those that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifHCOutUcastPkts"; + } + + leaf out-broadcast-pkts { + type oc-yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + broadcast address at this sub-layer, including those + that were discarded or not sent. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutBroadcastPkts"; + } + + + leaf out-multicast-pkts { + type oc-yang:counter64; + description + "The total number of packets that higher-level protocols + requested be transmitted, and that were addressed to a + multicast address at this sub-layer, including those + that were discarded or not sent. For a MAC-layer + protocol, this includes both Group and Functional + addresses. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - + ifHCOutMulticastPkts"; + } + + leaf out-discards { + type oc-yang:counter64; + description + "The number of outbound packets that were chosen to be + discarded even though no errors had been detected to + prevent their being transmitted. One possible reason + for discarding such a packet could be to free up buffer + space. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutDiscards"; + } + + leaf out-errors { + type oc-yang:counter64; + description + "For packet-oriented interfaces, the number of outbound + packets that could not be transmitted because of errors. + For character-oriented or fixed-length interfaces, the + number of outbound transmission units that could not be + transmitted because of errors. + + Discontinuities in the value of this counter can occur + at re-initialization of the management system, and at + other times as indicated by the value of + 'last-clear'."; + reference + "RFC 2863: The Interfaces Group MIB - ifOutErrors"; + } + + leaf carrier-transitions { + type oc-yang:counter64; + description + "Number of times the interface state has transitioned + between up and down since the time the device restarted + or the last-clear time, whichever is most recent."; + oc-ext:telemetry-on-change; + } + + leaf last-clear { + type oc-types:timeticks64; + units nanoseconds; + description + "Timestamp of the last time the interface counters were + cleared. + + The value is the timestamp in nanoseconds relative to + the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + oc-ext:telemetry-on-change; + } + } + } + + // data definition statements + + grouping sub-unnumbered-config { + description + "Configuration data for unnumbered subinterfaces"; + + leaf enabled { + type boolean; + default false; + description + "Indicates that the subinterface is unnumbered. By default + the subinterface is numbered, i.e., expected to have an + IP address configuration."; + } + } + + grouping sub-unnumbered-state { + description + "Operational state data unnumbered subinterfaces"; + } + + grouping sub-unnumbered-top { + description + "Top-level grouping unnumbered subinterfaces"; + + container unnumbered { + description + "Top-level container for setting unnumbered interfaces. + Includes reference the interface that provides the + address information"; + + container config { + description + "Configuration data for unnumbered interface"; + oc-ext:telemetry-on-change; + + uses sub-unnumbered-config; + } + + container state { + + config false; + + description + "Operational state data for unnumbered interfaces"; + + uses sub-unnumbered-config; + uses sub-unnumbered-state; + } + + uses oc-if:interface-ref; + } + } + + grouping subinterfaces-config { + description + "Configuration data for subinterfaces"; + + leaf index { + type uint32; + default 0; + description + "The index of the subinterface, or logical interface number. + On systems with no support for subinterfaces, or not using + subinterfaces, this value should default to 0, i.e., the + default subinterface."; + } + + uses interface-common-config; + + } + + grouping subinterfaces-state { + description + "Operational state data for subinterfaces"; + + oc-ext:operational; + + leaf name { + type string; + description + "The system-assigned name for the sub-interface. This MAY + be a combination of the base interface name and the + subinterface index, or some other convention used by the + system."; + oc-ext:telemetry-on-change; + } + + uses interface-common-state; + uses interface-counters-state; + } + + grouping subinterfaces-top { + description + "Subinterface data for logical interfaces associated with a + given interface"; + + container subinterfaces { + description + "Enclosing container for the list of subinterfaces associated + with a physical interface"; + + list subinterface { + key "index"; + + description + "The list of subinterfaces (logical interfaces) associated + with a physical interface"; + + leaf index { + type leafref { + path "../config/index"; + } + description + "The index number of the subinterface -- used to address + the logical interface"; + } + + container config { + description + "Configurable items at the subinterface level"; + oc-ext:telemetry-on-change; + + uses subinterfaces-config; + } + + container state { + + config false; + description + "Operational state data for logical interfaces"; + + uses subinterfaces-config; + uses subinterfaces-state; + } + } + } + } + + grouping interfaces-top { + description + "Top-level grouping for interface configuration and + operational state data"; + + container interfaces { + description + "Top level container for interfaces, including configuration + and state data."; + + + list interface { + key "name"; + + description + "The list of named interfaces on the device."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "References the configured name of the interface"; + //TODO: need to consider whether this should actually + //reference the name in the state subtree, which + //presumably would be the system-assigned name, or the + //configured name. Points to the config/name now + //because of YANG 1.0 limitation that the list + //key must have the same "config" as the list, and + //also can't point to a non-config node. + } + + container config { + description + "Configurable items at the global, physical interface + level"; + oc-ext:telemetry-on-change; + + uses interface-phys-config; + } + + container state { + + config false; + description + "Operational state data at the global interface level"; + + uses interface-phys-config; + uses interface-common-state; + uses interface-counters-state; + } + + uses interface-phys-holdtime-top; + uses subinterfaces-top; + } + } + } + + uses interfaces-top; + +} diff --git a/models/yang/openconfig-lldp.yang b/models/yang/openconfig-lldp.yang new file mode 100644 index 0000000000..e687b7c61b --- /dev/null +++ b/models/yang/openconfig-lldp.yang @@ -0,0 +1,660 @@ +module openconfig-lldp { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/lldp"; + + prefix "oc-lldp"; + + import openconfig-lldp-types { prefix oc-lldp-types; } + import openconfig-interfaces { prefix oc-if; } + import ietf-yang-types { prefix yang; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + for the LLDP protocol."; + + oc-ext:openconfig-version "0.2.1"; + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2018-07-17" { + description + "Adds ttl to lldp-neighbor-state"; + reference "0.2.0"; + } + + revision "2016-05-16" { + description + "Initial public revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + + // grouping statements + + grouping lldp-common-counters { + description + "Definition of global and per-interface counters"; + + leaf frame-in { + type yang:counter64; + description + "The number of lldp frames received."; + } + + leaf frame-out { + type yang:counter64; + description + "The number of frames transmitted out."; + } + + leaf frame-error-in { + type yang:counter64; + description + "The number of LLDP frames received with errors."; + } + + leaf frame-discard { + type yang:counter64; + description + "The number of LLDP frames received and discarded."; + } + + leaf tlv-discard { + type yang:counter64; + description + "The number of TLV frames received and discarded."; + } + + leaf tlv-unknown { + type yang:counter64; + description + "The number of frames received with unknown TLV."; + } + + leaf last-clear { + type yang:date-and-time; + description + "Indicates the last time the counters were + cleared."; + } + } + + grouping lldp-global-counters { + description + "Definition of global LLDP counters"; + + uses lldp-common-counters; + + leaf tlv-accepted { + type yang:counter64; + description + "The number of valid TLVs received."; + } + + leaf entries-aged-out { + type yang:counter64; + description + "The number of entries aged out due to timeout."; + } + + } + + grouping lldp-interface-counters { + description + "Definition of per-interface LLDP counters"; + + uses lldp-common-counters; + + leaf frame-error-out { + type yang:counter64; + description + "The number of frame transmit errors on the + interface."; + } + } + + grouping lldp-system-info-config { + description + "Configuration data for system-level local and remote + LLDP information"; + + leaf system-name { + type string { + length 0..255; + } + description + "The system name field shall contain an alpha-numeric string + that indicates the system's administratively assigned name. + The system name should be the system's fully qualified domain + name. If implementations support IETF RFC 3418, the sysName + object should be used for this field."; + } + + leaf system-description { + type string { + length 0..255; + } + description + "The system description field shall contain an alpha-numeric + string that is the textual description of the network entity. + The system description should include the full name and + version identification of the system's hardware type, + software operating system, and networking software. If + implementations support IETF RFC 3418, the sysDescr object + should be used for this field."; + } + + leaf chassis-id { + type string; + description + "The Chassis ID is a mandatory TLV which identifies the + chassis component of the endpoint identifier associated with + the transmitting LLDP agent"; + } + + leaf chassis-id-type { + type oc-lldp-types:chassis-id-type; + description + "This field identifies the format and source of the chassis + identifier string. It is an enumerator defined by the + LldpChassisIdSubtype object from IEEE 802.1AB MIB."; + } + } + + grouping lldp-system-info-state { + description + "Operational state data reported for the local and remote + systems"; + + } + + grouping lldp-neighbor-config { + description + "Configuration data for LLDP neighbors"; + + } + + grouping lldp-neighbor-state { + description + "Operational state data for LLDP neighbors"; + + leaf id { + type string; + description + "System generated identifier for the neighbor on the + interface."; + } + + leaf age { + type uint64; + units "seconds"; + description + "Age since discovery"; + } + + leaf last-update { + type int64; + description + "Seconds since last update received."; + } + + leaf ttl { + type uint16; + units "seconds"; + description + "The time-to-live (TTL) is a mandatory TLV which indicates + how long information from the neighbor should be considered + valid."; + } + + leaf port-id { + type string; + description + "The Port ID is a mandatory TLV which identifies the port + component of the endpoint identifier associated with the + transmitting LLDP agent. If the specified port is an IEEE + 802.3 Repeater port, then this TLV is optional."; + } + + leaf port-id-type { + type oc-lldp-types:port-id-type; + description + "This field identifies the format and source of the port + identifier string. It is an enumerator defined by the + PtopoPortIdType object from RFC2922."; + } + + leaf port-description { + type string; + description + "The binary string containing the actual port identifier for + the port which this LLDP PDU was transmitted. The source and + format of this field is defined by PtopoPortId from + RFC2922."; + } + + leaf management-address { + type string; + description + "The Management Address is a mandatory TLV which identifies a + network address associated with the local LLDP agent, which + can be used to reach the agent on the port identified in the + Port ID TLV."; + } + + leaf management-address-type { + type string; + description + "The enumerated value for the network address type + identified in this TLV. This enumeration is defined in the + 'Assigned Numbers' RFC [RFC3232] and the + ianaAddressFamilyNumbers object."; + } + } + + grouping lldp-capabilities-config { + description + "Configuration data for LLDP capabilities"; + } + + grouping lldp-capabilities-state { + description + "Operational state data for LLDP capabilities"; + + leaf name { + type identityref { + base oc-lldp-types:LLDP_SYSTEM_CAPABILITY; + } + description + "Name of the system capability advertised by the neighbor. + Capabilities are represented in a bitmap that defines the + primary functions of the system. The capabilities are + defined in IEEE 802.1AB."; + } + + leaf enabled { + type boolean; + description + "Indicates whether the corresponding system capability is + enabled on the neighbor."; + reference + "Sec 8.5.8.2 of IEEE 802.1AB-2009"; + } + } + + grouping lldp-capabilities-top { + description + "Top-level grouping for LLDP capabilities"; + + container capabilities { + config false; + description + "Enclosing container for list of LLDP capabilities"; + + list capability { + key "name"; + description + "List of LLDP system capabilities advertised by the + neighbor"; + + leaf name { + type leafref { + path "../state/name"; + } + description + "Reference to capabilities list key"; + } + + container config { + description + "Configuration data for LLDP capabilities"; + + uses lldp-capabilities-config; + } + + container state { + + config false; + + description + "Operational state data for LLDP capabilities"; + + uses lldp-capabilities-config; + uses lldp-capabilities-state; + } + } + } + } + + grouping lldp-custom-tlv-config { + description + "Configuration data for custom LLDP TLVs"; + } + + grouping lldp-custom-tlv-state { + description + "Operational state data for custom LLDP TLVs"; + + leaf type { + type int32; + description + "The integer value identifying the type of information + contained in the value field."; + } + + leaf oui { + type string; + description + "The organizationally unique identifier field shall contain + the organization's OUI as defined in Clause 9 of IEEE Std + 802. The high-order octet is 0 and the low-order 3 octets + are the SMI Network Management Private Enterprise Code of + the Vendor in network byte order, as defined in the + 'Assigned Numbers' RFC [RFC3232]."; + } + + leaf oui-subtype { + type string; + description + "The organizationally defined subtype field shall contain a + unique subtype value assigned by the defining organization."; + } + + // TODO: consider making this string type + leaf value { + type binary; + description + "A variable-length octet-string containing the + instance-specific information for this TLV."; + } + } + + grouping lldp-custom-tlv-top { + description + "Top-level grouping for custom LLDP TLVs"; + + container custom-tlvs { + config false; + description + "Enclosing container for list of custom TLVs from a + neighbor"; + + list tlv { + key "type oui oui-subtype"; + description + "List of custom LLDP TLVs from a neighbor"; + + leaf type { + type leafref { + path "../state/type"; + } + description + "Reference to type list key"; + } + + leaf oui { + type leafref { + path "../state/oui"; + } + description + "Reference to oui list key"; + } + + leaf oui-subtype { + type leafref { + path "../state/oui-subtype"; + } + description + "Reference to oui-subtype list key"; + } + + container config { + description + "Configuration data "; + + uses lldp-custom-tlv-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses lldp-custom-tlv-config; + uses lldp-custom-tlv-state; + } + } + } + } + + grouping lldp-neighbor-top { + description + "Top-level grouping for the LLDP neighbor list"; + + container neighbors { + config false; + description + "Enclosing container for list of LLDP neighbors on an + interface"; + + list neighbor { + key "id"; + description + "List of LLDP neighbors"; + + leaf id { + type leafref { + path "../state/id"; + } + description + " "; + } + + container config { + description + "Configuration data "; + + uses lldp-neighbor-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses lldp-system-info-config; + uses lldp-system-info-state; + uses lldp-neighbor-config; + uses lldp-neighbor-state; + } + + uses lldp-custom-tlv-top; + uses lldp-capabilities-top; + } + } + } + + grouping lldp-interface-config { + description + "Configuration data for LLDP on each interface"; + + leaf name { + type oc-if:base-interface-ref; + description + "Reference to the LLDP Ethernet interface"; + } + + leaf enabled { + type boolean; + default "true"; + description + "Enable or disable the LLDP protocol on the interface."; + } + } + + grouping lldp-interface-state { + description + "Operational state data for LLDP on each interface"; + + container counters { + description + "LLDP counters on each interface"; + + uses lldp-interface-counters; + } + } + + grouping lldp-interface-top { + description + "Top-level grouping "; + + container interfaces { + description + "Enclosing container "; + + list interface { + key "name"; + description + "List of interfaces on which LLDP is enabled / available"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for LLDP on each interface"; + + uses lldp-interface-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses lldp-interface-config; + uses lldp-interface-state; + } + + uses lldp-neighbor-top; + } + } + } + + + grouping lldp-config { + description + "Configuration data for global LLDP parameters"; + + leaf enabled { + type boolean; + default "true"; + description + "System level state of the LLDP protocol."; + } + + leaf hello-timer { + type uint64; + units "seconds"; + description + "System level hello timer for the LLDP protocol."; + } + + leaf-list suppress-tlv-advertisement { + type identityref { + base oc-lldp-types:LLDP_TLV; + } + description + "Indicates whether the local system should suppress the + advertisement of particular TLVs with the LLDP PDUs that it + transmits. Where a TLV type is specified within this list, it + should not be included in any LLDP PDU transmitted by the + local agent."; + } + } + + grouping lldp-state { + description + "Operational state data for global LLDP parameters"; + + container counters { + description + "Global LLDP counters"; + + uses lldp-global-counters; + } + } + + grouping lldp-top { + description + "Top-level grouping for LLDP model"; + + container lldp { + description + "Top-level container for LLDP configuration and state data"; + + container config { + description + "Configuration data "; + + uses lldp-config; + uses lldp-system-info-config; + } + + container state { + + config false; + + description + "Operational state data "; + + uses lldp-config; + uses lldp-system-info-config; + uses lldp-system-info-state; + uses lldp-state; + } + + uses lldp-interface-top; + } + } + + // data definition statements + + uses lldp-top; + + +} diff --git a/models/yang/openconfig-network-instance.yang b/models/yang/openconfig-network-instance.yang new file mode 100644 index 0000000000..1f04c9abef --- /dev/null +++ b/models/yang/openconfig-network-instance.yang @@ -0,0 +1,1122 @@ +module openconfig-network-instance { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/network-instance"; + + prefix "oc-netinst"; + + // import some basic types + //import ietf-inet-types { prefix inet; } + import ietf-yang-types { prefix "yang"; } + import ietf-inet-types { prefix "inet"; } + import openconfig-network-instance-types { prefix "oc-ni-types"; } + import openconfig-policy-types { prefix "oc-pol-types"; } + import openconfig-routing-policy { prefix "oc-rpol"; } + import openconfig-local-routing { prefix "oc-loc-rt"; } + import openconfig-interfaces { prefix "oc-if"; } + import openconfig-extensions { prefix "oc-ext"; } + import openconfig-network-instance-l3 { prefix "oc-ni-l3"; } + import openconfig-types { prefix "oc-types"; } + import openconfig-bgp { prefix "oc-bgp"; } + import openconfig-mpls { prefix "oc-mpls"; } + import openconfig-vlan { prefix "oc-vlan"; } + import openconfig-ospfv2 { prefix "oc-ospfv2"; } + import openconfig-policy-forwarding { prefix "oc-pf"; } + import openconfig-segment-routing { prefix "oc-sr"; } + import openconfig-isis { prefix "oc-isis"; } + import openconfig-aft { prefix "oc-aft"; } + import openconfig-pim { prefix "oc-pim"; } + import openconfig-igmp { prefix "oc-igmp"; } + + // include submodules + include openconfig-network-instance-l2; + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "An OpenConfig description of a network-instance. This may be + a Layer 3 forwarding construct such as a virtual routing and + forwarding (VRF) instance, or a Layer 2 instance such as a + virtual switch instance (VSI). Mixed Layer 2 and Layer 3 + instances are also supported."; + + oc-ext:openconfig-version "0.12.0"; + + revision "2019-04-16" { + description + "Move BGP RIB into the protocol/bgp container."; + reference "0.12.0"; + } + + revision "2019-02-03" { + description + "Expend netinst type description to link it to, for example, MPLS + service types."; + reference "0.11.2"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.11.1"; + } + + revision "2018-08-11" { + description + "Add vlan id as additional key in MAC table"; + reference "0.11.0"; + } + + revision "2018-06-22" { + description + "Fix typo in OSPF when statement"; + reference "0.10.2"; + } + + revision "2018-06-05" { + description + "Fix bugs in when statements"; + reference "0.10.1"; + } + + revision "2018-02-19" { + description + "Add PIM and IGMP to network instance"; + reference "0.10.0"; + } + + revision "2017-12-13" { + description + "Fix incorrect constraint on SR and MPLS containers"; + reference "0.9.0"; + } + + revision "2017-08-24" { + description + "Minor formatting fixes"; + reference "0.8.1"; + } + + revision "2017-02-28" { + description + "Add OSPFv2 to network instance"; + reference "0.8.0"; + } + + revision "2017-01-26" { + description + "Add policy forwarding to network instance"; + reference "0.7.0"; + } + + revision "2017-01-13" { + description + "Add AFT to the network instance"; + reference "0.6.0"; + } + + revision "2016-12-15" { + description + "Add segment routing to network instance"; + reference "0.5.0"; + } + + revision "2016-11-10" { + description + "Add IS-IS to OpenConfig network instance"; + reference "0.4.1"; + } + + revision "2016-10-12" { + description + "Update table connections"; + reference "0.4.0"; + } + + revision "2016-09-28" { + description + "Change L2 instance to submodule; add MAC table"; + reference "0.3.0"; + } + + revision "2016-08-11" { + description + "Resolve repeated container names in routing protocols"; + reference "0.2.3"; + } + + revision "2016-07-08" { + description + "Updated with refactored routing protocol models"; + reference "0.2.1"; + } + + revision "2016-03-29" { + description + "Initial revision"; + reference "0.2.0"; + } + + revision "2015-10-18" { + description + "Initial revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + typedef network-instance-ref { + type leafref { + path "/network-instances/network-instance/config/name"; + } + description + "A re-usable type that can be referenced within other + modules that references a network instance."; + } + + grouping network-instance-top { + description + "Top-level grouping containing a list of network instances."; + + container network-instances { + description + "The L2, L3, or L2+L3 forwarding instances that are + configured on the local system"; + + list network-instance { + key "name"; + + description + "Network instances configured on the local system"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "A unique name identifying the network instance"; + } + + uses l2ni-instance { + when "config/type = 'L2VSI' or config/type = 'L2P2P'" + + " or config/type = 'L2L3' or config/type = 'DEFAULT_INSTANCE'" { + description + "Layer 2 configuration parameters included when + a network instance is a Layer 2 instance or a + combined L2L3 instance"; + } + } + + container config { + description + "Configuration parameters relating to a network + instance"; + uses network-instance-config; + uses network-instance-type-dependent-config; + } + + + container state { + config false; + description + "Operational state parameters relating to a network + instance"; + uses network-instance-config; + uses network-instance-type-dependent-config; + uses network-instance-state; + } + + container encapsulation { + when "../config/type != 'DEFAULT_INSTANCE'" { + description + "Only allow the encapsulation of the instance to be + set when the instance is not the default instance"; + } + description + "Configuration parameters relating to the encapsulation + used for the network instance"; + + container config { + description + "Configuration parameters relating to the encapsulation + of the network instance"; + + uses encapsulation-config; + + uses l2ni-encapsulation-config { + when "../../config/type = 'L2VSI' or ../../config/type = 'L2P2P' + or ../../config/type = 'L2L3'" { + description + "Only allow L2 encapsulations to be set + when the instance is of a type that supports + L2"; + } + } + } + + container state { + config false; + description + "State parameters relating to the encapsulation of + the network instance"; + uses encapsulation-config; + + uses l2ni-encapsulation-config { + when "../../config/type = 'L2VSI' or ../../config/type = 'L2P2P' + or ../../config/type = 'L2L3'" { + description + "Only allow L2 encapsulations to be set + when the instance is of a type that supports + L2"; + } + } + } + } + + container inter-instance-policies { + description + "Policies dictating how RIB or FIB entries are imported + to and exported from this instance"; + + uses oc-rpol:apply-policy-group; + } + + container table-connections { + description + "Policies dictating how RIB or FIB entries are propagated + between tables"; + + list table-connection { + key "src-protocol dst-protocol address-family"; + + description + "A list of connections between pairs of routing or + forwarding tables, the leaking of entries between + which is specified by the import policy. + + A connection connecting a source table to a destination + table implies that routes that match the policy specified + for the connection are available for the destination + protocol to advertise, or match within its policies."; + + leaf src-protocol { + type leafref { + path "../config/src-protocol"; + } + description + "The name of the protocol associated with the table + which should be utilised as the source of forwarding + or routing information"; + } + + leaf dst-protocol { + type leafref { + path "../config/dst-protocol"; + } + description + "The table to which routing entries should be + exported"; + } + + leaf address-family { + type leafref { + path "../config/address-family"; + } + description + "The address family associated with the connection"; + } + + container config { + description + "Configuration parameters relating to the connection + between tables"; + uses inter-table-policies-config; + } + container state { + config false; + description + "State parameters relating to the connection between + tables"; + uses inter-table-policies-config; + } + } + } + + container interfaces { + description + "The interfaces that are associated with this network + instance"; + + list interface { + key "id"; + unique "config/interface config/subinterface"; + + description + "An interface associated with the network instance"; + + leaf id { + type leafref { + path "../config/id"; + } + description + "A reference to an identifier for this interface which + acts as a key for this list"; + } + + container config { + description + "Configuration parameters relating to the associated + interface"; + uses instance-interfaces-config; + } + + container state { + config false; + description + "Operational state parameters relating to the + associated interface"; + + uses instance-interfaces-config; + uses instance-interfaces-state; + } + } + } + + uses oc-ni-l3:l3ni-route-limit-structural { + when "type = 'L3VRF' or type = 'L2L3'" { + description + "Layer 3 VRF or L2/L3 instances can have route limits + applied. This is not supported for the default instance."; + } + } + + container tables { + description + "The routing tables that are managed by this network + instance"; + + list table { + key "protocol address-family"; + + description + "A network instance manages one or more forwarding or + routing tables. These may reflect a Layer 2 forwarding + information base, a Layer 3 routing table, or an MPLS + LFIB. + + The table populated by a protocol within an instance is + identified by the protocol identifier (e.g., BGP, IS-IS) + and the address family (e.g., IPv4, IPv6) supported by + that protocol. Multiple instances of the same protocol + populate a single table -- such that + a single IS-IS or OSPF IPv4 table exists per network + instance. + + An implementation is expected to create entries within + this list when the relevant protocol context is enabled. + i.e., when a BGP instance is created with IPv4 and IPv6 + address families enabled, the protocol=BGP, + address-family=IPv4 table is created by the system."; + + leaf protocol { + type leafref { + path "../config/protocol"; + } + description + "A reference to the protocol that populates + the table"; + } + + leaf address-family { + type leafref { + path "../config/address-family"; + } + description + "A reference to the address-family that the + table represents"; + } + + container config { + description + "Configuration parameters relating to the + table"; + uses table-config; + } + + container state { + config false; + description + "State parameters related to the table"; + uses table-config; + } + } + } + + container connection-points { + description + "The set of connection points within a forwarding + instance"; + + list connection-point { + key "connection-point-id"; + + description + "A connection point within a Layer 2 network instance. + Each connection-point consists of a set of interfaces + only one of which is active at any one time. Other than + the specification of whether an interface is local + (i.e., exists within this network-instance), or remote, + all configuration and state parameters are common"; + + leaf connection-point-id { + type leafref { + path "../config/connection-point-id"; + } + description + "A locally significant reference for the + connection-point"; + } + + container config { + description + "Configuration parameters relating to a Layer 2 + network instance connection point"; + uses instance-connection-point-config; + } + container state { + config false; + description + "Operational state parameters relating to a Layer 2 + network instance connection point"; + + uses instance-connection-point-config; + uses instance-connection-point-state; + } + + container endpoints { + when "../../../config/type = 'L2P2P' " + + "or ../../../config/type = 'L2VSI'" { + description + "Configuration parameters to associate interfaces + into a common group for use in Layer 2 network + instances"; + } + + description + "The set of endpoints which are grouped within the + connection point"; + + list endpoint { + key "endpoint-id"; + + description + "A list of the endpoints (interfaces or remote + connection points that can be used for this + connection point). The active endpoint is selected + based on the precedence that it is configured + with"; + + leaf endpoint-id { + type leafref { + path "../config/endpoint-id"; + } + description + "A pointer to the configured identifier for the + endpoint"; + } + + container config { + description + "Configuration parameters relating to the + endpoint"; + uses instance-endpoint-config; + } + container state { + config false; + description + "Operational state parameters relating to the + endpoint"; + uses instance-endpoint-config; + uses instance-endpoint-state; + } + + container local { + when "../config/type = 'LOCAL'" { + description + "Only include the local configuration when + the endpoint is specified to be local to + the network element"; + } + + description + "Configuration and operational state parameters + relating to a local interface"; + + container config { + description + "Configuration parameters relating to a local + endpoint"; + uses instance-endpoint-local-config; + } + + container state { + config false; + description + "Operational state parameters relating to a + local endpoint"; + uses instance-endpoint-local-config; + } + } + + container remote { + when "../config/type = 'REMOTE'" { + description + "Only include the remote configuration when + the endpoint is specified to be remote to + the network element"; + } + + description + "Configuration and operational state parameters + relating to a remote interface"; + + container config { + description + "Configuration parameters relating to a remote + endpoint"; + uses instance-endpoint-remote-config; + } + + container state { + config false; + description + "Operational state parameters relating to + a remote endpoint"; + uses instance-endpoint-remote-config; + } + } + } + } + } + } + + uses oc-mpls:mpls-top { + when "config/type = 'DEFAULT_INSTANCE'" { + description + "MPLS configuration is only valid within the default + network instance."; + } + } + + uses oc-sr:sr-top { + when "config/type = 'DEFAULT_INSTANCE'" { + description + "Segment routing configuration is only valid with the default + network instance."; + } + } + + uses oc-vlan:vlan-top; + + uses oc-pf:policy-forwarding-top; + + uses oc-aft:aft-top; + + container protocols { + description + "The routing protocols that are enabled for this + network-instance."; + + list protocol { + key "identifier name"; + + description + "A process (instance) of a routing protocol. Some + systems may not support more than one instance of + a particular routing protocol"; + + leaf identifier { + type leafref { + path "../config/identifier"; + } + description + "The protocol name for the routing or forwarding + protocol to be instantiated"; + } + + leaf name { + type leafref { + path "../config/name"; + } + description + "An operator-assigned identifier for the routing + or forwarding protocol. For some processes this + leaf may be system defined."; + } + + container config { + description + "Configuration parameters relating to the routing + protocol instance"; + + uses protocols-config; + } + + container state { + config false; + description + "State parameters relating to the routing protocol + instance"; + + uses protocols-config; + uses protocols-state; + } + + uses oc-loc-rt:local-static-top { + when "config/identifier = 'STATIC'" { + description + "Include static route parameters only when the + protocol is set to static"; + } + description + "Configuration and state parameters relating to + static routes"; + } + + uses oc-loc-rt:local-aggregate-top { + when "config/identifier = 'LOCAL_AGGREGATE'" { + description + "Include aggregate route parameters only when the + protocol is set to aggregate"; + } + description + "Configuration and state parameters relating to + locally generated aggregate routes"; + } + + uses oc-bgp:bgp-top { + when "config/identifier = 'BGP'" { + description + "Include BGP parameters only when the protocol + is of type BGP"; + } + description + "Configuration and state parameters relating to + Border Gateway Protocol (BGP)"; + } + + uses oc-ospfv2:ospfv2-top { + when "config/identifier = 'OSPF'" { + description + "Include OSPFv2 parameters only when the protocol + is of type OSPFv2"; + } + } + + uses oc-isis:isis-top { + when "config/identifier = 'ISIS'" { + description + "Include IS-IS configuration when the protocol is of type + IS-IS"; + } + description + "Configuration and state parameters relating to Intermediate + System to Intermediate System (IS-IS)."; + } + + uses oc-pim:pim-top { + when "config/identifier = 'PIM'" { + description + "Include PIM configuration when the protocol is of type + PIM"; + } + description + "Configuration and state parameters relating to Protocol + Indepdendent Multicast (PIM)."; + } + + uses oc-igmp:igmp-top { + when "config/identifier = 'IGMP'" { + description + "Include IGMP configuration when the protocol is of type + IGMP"; + } + description + "Configuration and state parameters relating to the Internet + Group Management Protocol (IGMP)."; + } + } + } + } + } + } + + grouping network-instance-type-dependent-config { + description + "Type dependent network instance configuration"; + + uses oc-ni-l3:l3ni-instance-common-config { + when "type = 'L3VRF' or type = 'L2L3'" { + description + "Layer 3 VRF configuration parameters included when a + network instance is a L3VRF or combined L2L3 instance"; + } + } + + uses l2ni-instance-common-config { + when "type = 'L2VSI' or type = 'L2P2P'" + + " or type = 'L2L3'" { + description + "Layer 2 configuration parameters included when + a network instance is a Layer 2 instance or a + combined L2L3 instance"; + } + } + } + + grouping instance-endpoint-config { + description + "Configuration data relating to an forwarding-instance + endpoint"; + + leaf endpoint-id { + type string; + description + "An identifier for the endpoint"; + } + + leaf precedence { + type uint16; + description + "The precedence of the endpoint - the lowest precendence + viable endpoint will be utilised as the active endpoint + within a connection"; + } + + leaf type { + type identityref { + base "oc-ni-types:ENDPOINT_TYPE"; + } + description + "The type of endpoint that is referred to by the current + endpoint"; + } + + } + + grouping instance-endpoint-local-config { + description + "Configuration parameters relating to an endpoint that is local + to the current system"; + + uses oc-if:interface-ref-common; + } + + grouping instance-endpoint-remote-config { + description + "Configuration parameters relating to an endpoint that is + remote from the local system"; + leaf remote-system { + type inet:ip-address; + description + "The IP address of the device which hosts the + remote end-point"; + } + + leaf virtual-circuit-identifier { + type uint32; + description + "The virtual-circuit identifier that identifies the + connection at the remote end-point"; + } + } + + grouping instance-endpoint-state { + description + "Operational state data relating to a forwarding-instance + endpoint"; + leaf active { + type boolean; + description + "When the backup endpoint is active, the value of this + parameter is set to true"; + } + } + + grouping instance-connection-point-config { + description + "Configuration data relating to a forwarding-instance + connection point"; + + leaf connection-point-id { + type string; + description + "An identifier for a connection point"; + } + } + + grouping instance-connection-point-state { + description + "Operational state data relating to a forwarding-instance + connection point"; + } + + grouping table-config { + description + "Config parameters relating to an L2/L2.5/L3 table that exists + within a network instance"; + + leaf protocol { + type leafref { + path "../../../../protocols/protocol/config/identifier"; + } + description + "Reference to the protocol that the table is associated with."; + } + + leaf address-family { + type identityref { + base oc-types:ADDRESS_FAMILY; + } + description + "The address family (IPv4, IPv6) of the table's entries"; + } + } + + grouping instance-interfaces-config { + description + "Configuration parameters related to an interface associated + with the network instance"; + + leaf id { + type string; + description + "A unique identifier for this interface - this is expressed + as a free-text string"; + } + + uses oc-if:interface-ref-common; + + leaf-list associated-address-families { + type identityref { + base oc-types:ADDRESS_FAMILY; + } + description + "The address families on the subinterface which are to be + associated with this network instance. When this leaf-list + is empty and the network instance requires Layer 3 information + the address families for which the network instance is + enabled should be imported. If the value of this leaf-list + is specified then the association MUST only be made for + those address families that are included in the list."; + } + } + + grouping instance-interfaces-state { + description + "Operational state parameters relating to an interface + associated with this network instance"; + } + + grouping inter-table-policies-config { + description + "Configuration entries that relate to how RIB or FIB entries + are propagated between tables within the same network + instance"; + + leaf src-protocol { + type leafref { + // we are at table-connections/table-connection/config/. + path "../../../../tables/table/config/protocol"; + } + description + "The source protocol for the table connection"; + } + + leaf address-family { + type leafref { + // we are at table-connections/table-connection/config/. + path "../../../../tables/" + + "table[protocol=current()/../src-protocol]/" + + "config/address-family"; + } + description + "The address family associated with the connection. This + must be defined for the source protocol. The target + address family is implicitly defined by the address family + specified for the source protocol."; + } + + leaf dst-protocol { + type leafref { + path "../../../../tables/table/config/protocol"; + } + description + "The destination protocol for the table connection"; + } + + uses oc-rpol:apply-policy-import-config; + } + + grouping network-instance-config { + description + "Configuration parameters relating to a top-level network + instance"; + + leaf name { + type string; + description + "An operator-assigned unique name for the forwarding + instance"; + } + + leaf type { + type identityref { + base "oc-ni-types:NETWORK_INSTANCE_TYPE"; + } + description + "The type of network instance. The value of this leaf + indicates the type of forwarding entries that should be + supported by this network instance. Signalling protocols + also use the network instance type to infer the type of + service they advertise; for example MPLS signalling + for an L2VSI network instance would infer a VPLS service + whereas a type of L2PTP would infer a VPWS (pseudo-wire) + service"; + } + + leaf enabled { + type boolean; + description + "Whether the network instance should be configured to be + active on the network element"; + } + + leaf description { + type string; + description + "A free-form string to be used by the network operator to + describe the function of this network instance"; + } + + leaf router-id { + type yang:dotted-quad; + description + "A identifier for the local network instance - typically + used within associated routing protocols or signalling + routing information in another network instance"; + } + + leaf route-distinguisher { + type oc-ni-types:route-distinguisher; + description + "The route distinguisher that should be used for the local + VRF or VSI instance when it is signalled via BGP."; + } + } + + grouping network-instance-state { + description + "Operational state parameters relating to a network instance"; + } + + grouping protocols-config { + description + "Configuration parameters relating to a generic protocol + instance within a network instance"; + + leaf identifier { + type identityref { + base "oc-pol-types:INSTALL_PROTOCOL_TYPE"; + } + description + "The protocol identifier for the instance"; + } + + leaf name { + type string; + description + "A unique name for the protocol instance"; + } + + leaf enabled { + type boolean; + description + "A boolean value indicating whether the local protocol + instance is enabled."; + } + + leaf default-metric { + type uint32; + description + "The default metric within the RIB for entries that are + installed by this protocol instance. This value may + be overridden by protocol specific configuration options. + The lower the metric specified the more preferable the RIB + entry is to be selected for use within the network instance. + Where multiple entries have the same metric value then these + equal cost paths should be treated according to the specified + ECMP path selection behaviour for the instance"; + } + } + + grouping protocols-state { + description + "Operational state parameters relating to a protocol instance"; + } + + grouping instance-interface-association-config { + description + "Grouping containing leaves that are to be augmented into an + interface or subinterface to include mapping to a network + instance"; + + leaf network-instance { + type leafref { + path "/network-instances/network-instance/name"; + } + description + "The network instance that this interface is associated + with"; + } + } + + grouping encapsulation-config { + description + "Type agnostic configuration parameters relating to the + encapsulation of the network instance"; + + leaf encapsulation-type { + type identityref { + base oc-ni-types:ENCAPSULATION; + } + description + "The on-the-wire encapsulation that should be used when + sending traffic from this network instance"; + } + + // rjs: This is left here as I suspect that this can + // be used in EVPN. Need to validate implementations, otherwise + // move to L3. (TODO) + leaf label-allocation-mode { + type identityref { + base oc-ni-types:LABEL_ALLOCATION_MODE; + } + description + "The label allocation mode to be used for L3 entries + in the network instance"; + } + } + + uses network-instance-top; +} diff --git a/models/yang/openconfig-platform.yang b/models/yang/openconfig-platform.yang new file mode 100644 index 0000000000..ecf38cd1af --- /dev/null +++ b/models/yang/openconfig-platform.yang @@ -0,0 +1,779 @@ +module openconfig-platform { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/platform"; + + prefix "oc-platform"; + + import openconfig-platform-types { prefix oc-platform-types; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-alarm-types { prefix oc-alarm-types; } + import openconfig-yang-types { prefix oc-yang; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines a data model for representing a system + component inventory, which can include hardware or software + elements arranged in an arbitrary structure. The primary + relationship supported by the model is containment, e.g., + components containing subcomponents. + + It is expected that this model reflects every field replacable + unit on the device at a minimum (i.e., additional information + may be supplied about non-replacable components). + + Every element in the inventory is termed a 'component' with each + component expected to have a unique name and type, and optionally + a unique system-assigned identifier and FRU number. The + uniqueness is guaranteed by the system within the device. + + Components may have properties defined by the system that are + modeled as a list of key-value pairs. These may or may not be + user-configurable. The model provides a flag for the system + to optionally indicate which properties are user configurable. + + Each component also has a list of 'subcomponents' which are + references to other components. Appearance in a list of + subcomponents indicates a containment relationship as described + above. For example, a linecard component may have a list of + references to port components that reside on the linecard. + + This schema is generic to allow devices to express their own + platform-specific structure. It may be augmented by additional + component type-specific schemas that provide a common structure + for well-known component types. In these cases, the system is + expected to populate the common component schema, and may + optionally also represent the component and its properties in the + generic structure. + + The properties for each component may include dynamic values, + e.g., in the 'state' part of the schema. For example, a CPU + component may report its utilization, temperature, or other + physical properties. The intent is to capture all platform- + specific physical data in one location, including inventory + (presence or absence of a component) and state (physical + attributes or status)."; + + oc-ext:openconfig-version "0.12.2"; + + revision "2019-04-16" { + description + "Fix bug in parent path reference"; + reference "0.12.2"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.12.1"; + } + + revision "2018-06-29" { + description + "Added location description for components"; + reference "0.12.0"; + } + + revision "2018-06-03" { + description + "Added parent reference, empty flag and preconfiguration + for components"; + reference "0.11.0"; + } + + revision "2018-04-20" { + description + "Added new per-component state data: mfg-date and removable"; + reference "0.10.0"; + } + + revision "2018-01-30" { + description + "Amended approach for modelling CPU - rather than having + a local CPU utilisation state variable, a component with + a CPU should create a subcomponent of type CPU to report + statistics."; + reference "0.9.0"; + } + + revision "2018-01-16" { + description + "Added new per-component common data; add temp alarm; + moved hardware-port reference to port model"; + reference "0.8.0"; + } + + revision "2017-12-14" { + description + "Added anchor containers for component data, added new + component types"; + reference "0.7.0"; + } + + revision "2017-08-16" { + description + "Added power state enumerated type"; + reference "0.6.0"; + } + + revision "2016-12-22" { + description + "Added temperature state variable to component"; + reference "0.5.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // grouping statements + + + grouping platform-component-properties-config { + description + "System-defined configuration data for component properties"; + + leaf name { + type string; + description + "System-supplied name of the property -- this is typically + non-configurable"; + } + + leaf value { + type union { + type string; + type boolean; + type int64; + type uint64; + type decimal64 { + fraction-digits 2; + } + } + description + "Property values can take on a variety of types. Signed and + unsigned integer types may be provided in smaller sizes, + e.g., int8, uint16, etc."; + } + } + + grouping platform-component-properties-state { + description + "Operational state data for component properties"; + + leaf configurable { + type boolean; + description + "Indication whether the property is user-configurable"; + } + } + + grouping platform-component-properties-top { + description + "Top-level grouping "; + + container properties { + description + "Enclosing container "; + + list property { + key "name"; + description + "List of system properties for the component"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the property name."; + } + + container config { + description + "Configuration data for each property"; + + uses platform-component-properties-config; + } + + container state { + + config false; + + description + "Operational state data for each property"; + + uses platform-component-properties-config; + uses platform-component-properties-state; + } + } + } + } + + grouping platform-subcomponent-ref-config { + description + "Configuration data for subcomponent references"; + + leaf name { + type leafref { + path "../../../../../component/config/name"; + } + description + "Reference to the name of the subcomponent"; + } + } + + grouping platform-subcomponent-ref-state { + description + "Operational state data for subcomponent references"; + + } + + grouping platform-subcomponent-ref-top { + description + "Top-level grouping for list of subcomponent references"; + + container subcomponents { + description + "Enclosing container for subcomponent references"; + + list subcomponent { + key "name"; + description + "List of subcomponent references"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the name list key"; + } + + container config { + description + "Configuration data for the subcomponent"; + + uses platform-subcomponent-ref-config; + } + + container state { + + config false; + + description + "Operational state data for the subcomponent"; + + uses platform-subcomponent-ref-config; + uses platform-subcomponent-ref-state; + } + } + } + } + + grouping platform-component-config { + description + "Configuration data for components"; + + leaf name { + type string; + description + "Device name for the component -- this may not be a + configurable parameter on many implementations. Where + component preconfiguration is supported, for example, + the component name may be configurable."; + } + } + + grouping platform-component-state { + description + "Operational state data for device components."; + + leaf type { + type union { + type identityref { + base oc-platform-types:OPENCONFIG_HARDWARE_COMPONENT; + } + type identityref { + base oc-platform-types:OPENCONFIG_SOFTWARE_COMPONENT; + } + } + description + "Type of component as identified by the system"; + } + + leaf id { + type string; + description + "Unique identifier assigned by the system for the + component"; + } + + leaf location { + type string; + description + "System-supplied description of the location of the + component within the system. This could be a bay position, + slot number, socket location, etc. For component types that + have an explicit slot-id attribute, such as linecards, the + system should populate the more specific slot-id."; + } + + leaf description { + type string; + description + "System-supplied description of the component"; + } + + leaf mfg-name { + type string; + description + "System-supplied identifier for the manufacturer of the + component. This data is particularly useful when a + component manufacturer is different than the overall + device vendor."; + } + + leaf mfg-date { + type oc-yang:date; + description + "System-supplied representation of the component's + manufacturing date."; + } + + leaf hardware-version { + type string; + description + "For hardware components, this is the hardware revision of + the component."; + } + + leaf firmware-version { + type string; + description + "For hardware components, this is the version of associated + firmware that is running on the component, if applicable."; + } + + leaf software-version { + type string; + description + "For software components such as operating system or other + software module, this is the version of the currently + running software."; + } + + leaf serial-no { + type string; + description + "System-assigned serial number of the component."; + } + + leaf part-no { + type string; + description + "System-assigned part number for the component. This should + be present in particular if the component is also an FRU + (field replaceable unit)"; + } + + leaf removable { + type boolean; + description + "If true, this component is removable or is a field + replaceable unit"; + } + + leaf oper-status { + type identityref { + base oc-platform-types:COMPONENT_OPER_STATUS; + } + description + "If applicable, this reports the current operational status + of the component."; + } + + leaf empty { + type boolean; + default false; + description + "The empty leaf may be used by the device to indicate that a + component position exists but is not populated. Using this + flag, it is possible for the management system to learn how + many positions are available (e.g., occupied vs. empty + linecard slots in a chassis)."; + } + + leaf parent { + type leafref { + path "../../../component/config/name"; + } + description + "Reference to the name of the parent component. Note that + this reference must be kept synchronized with the + corresponding subcomponent reference from the parent + component."; + } + } + + grouping platform-component-temp-alarm-state { + description + "Temperature alarm data for platform components"; + + // TODO(aashaikh): consider if these leaves could be in a + // reusable grouping (not temperature-specific); threshold + // may always need to be units specific. + + leaf alarm-status { + type boolean; + description + "A value of true indicates the alarm has been raised or + asserted. The value should be false when the alarm is + cleared."; + } + + leaf alarm-threshold { + type uint32; + description + "The threshold value that was crossed for this alarm."; + } + + leaf alarm-severity { + type identityref { + base oc-alarm-types:OPENCONFIG_ALARM_SEVERITY; + } + description + "The severity of the current alarm."; + } + } + + grouping platform-component-power-state { + description + "Power-related operational state for device components."; + + leaf allocated-power { + type uint32; + units watts; + description + "Power allocated by the system for the component."; + } + + leaf used-power { + type uint32; + units watts; + description + "Actual power used by the component."; + } + } + + grouping platform-component-temp-state { + description + "Temperature state data for device components"; + + container temperature { + description + "Temperature in degrees Celsius of the component. Values include + the instantaneous, average, minimum, and maximum statistics. If + avg/min/max statistics are not supported, the target is expected + to just supply the instant value"; + + uses oc-platform-types:avg-min-max-instant-stats-precision1-celsius; + uses platform-component-temp-alarm-state; + } + } + + grouping platform-component-memory-state { + description + "Per-component memory statistics"; + + container memory { + description + "For components that have associated memory, these values + report information about available and utilized memory."; + + leaf available { + type uint64; + units bytes; + description + "The available memory physically installed, or logically + allocated to the component."; + } + + // TODO(aashaikh): consider if this needs to be a + // min/max/avg statistic + leaf utilized { + type uint64; + units bytes; + description + "The memory currently in use by processes running on + the component, not considering reserved memory that is + not available for use."; + } + } + } + + grouping platform-anchors-top { + description + "This grouping is used to add containers for components that + are common across systems, but do not have a defined schema + within the openconfig-platform module. Containers should be + added to this grouping for components that are expected to + exist in multiple systems, with corresponding modules + augmenting the config/state containers directly."; + + container chassis { + description + "Data for chassis components"; + + container config { + description + "Configuration data for chassis components"; + } + + container state { + config false; + description + "Operational state data for chassis components"; + } + } + +// TODO(aashaikh): linecard container is already defined in +// openconfig-platform-linecard; will move to this module +// in future. + /* + container linecard { + description + "Data for linecard components"; + + container config { + description + "Configuration data for linecard components"; + } + + container state { + config false; + description + "Operational state data for linecard components"; + } + } + */ + + container port { + description + "Data for physical port components"; + + container config { + description + "Configuration data for physical port components"; + } + + container state { + config false; + description + "Operational state data for physical port components"; + } + } + +// TODO(aashaikh): transceiver container is already defined in +// openconfig-platform-transceiver; will move to this module +// in future. + /* + container transceiver { + description + "Data for transceiver components"; + + container config { + description + "Configuration data for transceiver components"; + } + + container state { + config false; + description + "Operational state data for transceiver components"; + } + } + */ + + container power-supply { + description + "Data for power supply components"; + + container config { + description + "Configuration data for power supply components"; + } + + container state { + config false; + description + "Operational state data for power supply components"; + } + } + + container fan { + description + "Data for fan components"; + + container config { + description + "Configuration data for fan components"; + } + + container state { + config false; + description + "Operational state data for fan components"; + } + } + + container fabric { + description + "Data for fabric components"; + + container config { + description + "Configuration data for fabric components"; + } + + container state { + config false; + description + "Operational state data for fabric components"; + } + } + + container storage { + description + "Data for storage components"; + + container config { + description + "Configuration data for storage components"; + } + + container state { + config false; + description + "Operational state data for storage components"; + } + } + + container cpu { + description + "Data for cpu components"; + + container config { + description + "Configuration data for cpu components"; + } + + container state { + config false; + description + "Operational state data for cpu components"; + } + } + + container integrated-circuit { + description + "Data for chip components, such as ASIC, NPUs, etc."; + + container config { + description + "Configuration data for chip components"; + } + + container state { + config false; + description + "Operational state data for chip components"; + } + } + + container backplane { + description + "Data for backplane components"; + + container config { + description + "Configuration data for backplane components"; + } + + container state { + config false; + description + "Operational state data for backplane components"; + } + } + } + + grouping platform-component-top { + description + "Top-level grouping for components in the device inventory"; + + container components { + description + "Enclosing container for the components in the system."; + + list component { + key "name"; + description + "List of components, keyed by component name."; + + leaf name { + type leafref { + path "../config/name"; + } + description + "References the component name"; + } + + container config { + description + "Configuration data for each component"; + + uses platform-component-config; + } + + container state { + + config false; + + description + "Operational state data for each component"; + + uses platform-component-config; + uses platform-component-state; + uses platform-component-temp-state; + uses platform-component-memory-state; + uses platform-component-power-state; + } + + uses platform-component-properties-top; + uses platform-subcomponent-ref-top; + uses platform-anchors-top; + } + } + } + + + // data definition statements + + uses platform-component-top; + + + // augments + + +} diff --git a/models/yang/openconfig-spanning-tree-ext.yang b/models/yang/openconfig-spanning-tree-ext.yang new file mode 100755 index 0000000000..27adfa25f5 --- /dev/null +++ b/models/yang/openconfig-spanning-tree-ext.yang @@ -0,0 +1,289 @@ +module openconfig-spanning-tree-ext { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/spanning-tree/extension"; + + prefix "oc-stp-ext"; + + import openconfig-spanning-tree { prefix oc-stp; } + import openconfig-spanning-tree-types { prefix oc-stp-types; } + import openconfig-yang-types { prefix oc-yang; } + + + identity PVST { + base oc-stp-types:STP_PROTOCOL; + description + "Per Vlan Spanning Tree Protocol"; + } + + grouping stp-global-ext-config { + leaf rootguard-timeout { + type uint16 { + range 5..600; + } + units "seconds"; + default 30; + } + uses oc-stp:stp-timer-config; + uses oc-stp:bridge-priority-config; + } + + grouping spanning-tree-enable-config { + leaf spanning-tree-enable { + type boolean; + description + "Enable/Disable spanning tree protocol on an interface or Vlan"; + } + } + + grouping stp-interface-common-ext-config { + leaf portfast { + type boolean; + description + "Enable/Disable portfast"; + } + + leaf uplink-fast { + type boolean; + description + "Enable/Disable uplink fast"; + } + + leaf bpdu-guard-port-shutdown { + type boolean; + description + "Port to be shutdown when it receives a BPDU"; + } + + leaf cost { + type uint32 { + range 1..200000000; + } + description + "The port's contribution, when it is the Root Port, + to the Root Path Cost for the Bridge"; + reference + "IEEE 802.1D 17.13.11 PortPathCost"; + } + + leaf port-priority { + type oc-stp-types:stp-port-priority-type; + description + "The manageable component of the Port Identifier, + also known as the Port Priority"; + reference + "IEEE 802.1D 17.13.10 Port Identifier Priority"; + } + + uses spanning-tree-enable-config; + } + + grouping stp-instance-state { + leaf stp-instance { + type uint16; + description + "Instance identifier of STP"; + } + + leaf root-port-name { + type string; + description + "Name of root port"; + } + } + + grouping vlan-interface-extra-state-field { + leaf root-guard-timer { + type uint16; + description + "Root guard current timer value"; + } + } + + grouping vlan-interface-extra-counters { + leaf tcn-sent { + type oc-yang:counter64; + description + "Tcn transmitted"; + } + + leaf tcn-received { + type oc-yang:counter64; + description + "Tcn received"; + } + } + + grouping rpvst-vlan-interface-extra-counters { + leaf config-bpdu-sent { + type oc-yang:counter64; + description + "Config BPDU transmitted"; + } + + leaf config-bpdu-received { + type oc-yang:counter64; + description + "Config BPDU received"; + } + } + + grouping stp-pvst-top { + description + "Top grouping for per vlan spanning tree configuration + and operation data"; + + list vlan { + key "vlan-id"; + description + "List of the vlans"; + + leaf vlan-id { + type leafref { + path "../config/vlan-id"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for each vlan"; + + uses oc-stp:stp-rapid-pvst-config; + uses oc-stp:stp-timer-config; + uses oc-stp:bridge-priority-config; + uses spanning-tree-enable-config; + } + + container state { + config false; + description + "Operational data for each vlan"; + + uses oc-stp:stp-rapid-pvst-config; + uses oc-stp:stp-timer-config; + uses oc-stp:bridge-priority-config; + uses oc-stp:stp-common-state; + uses spanning-tree-enable-config; + uses stp-instance-state; + } + + uses oc-stp:stp-interfaces-top; + } + } + + augment /oc-stp:stp { + container pvst { + description + "Per vlan Spanning-tree protocol configuration and + operational data"; + + uses stp-pvst-top; + } + } + + augment /oc-stp:stp/oc-stp:global/oc-stp:config { + uses stp-global-ext-config; + } + + augment /oc-stp:stp/oc-stp:global/oc-stp:state { + uses stp-global-ext-config; + } + + augment /oc-stp:stp/oc-stp:rapid-pvst/oc-stp:vlan/oc-stp:config { + uses spanning-tree-enable-config; + } + + augment /oc-stp:stp/oc-stp:rapid-pvst/oc-stp:vlan/oc-stp:state { + uses stp-instance-state; + } + + augment /oc-stp:stp/oc-stp:rapid-pvst/oc-stp:vlan/oc-stp:interfaces/oc-stp:interface/oc-stp:state { + uses vlan-interface-extra-state-field; + } + + augment /oc-stp:stp/oc-stp:rapid-pvst/oc-stp:vlan/oc-stp:interfaces/oc-stp:interface/oc-stp:state/oc-stp:counters { + uses vlan-interface-extra-counters; + uses rpvst-vlan-interface-extra-counters; + } + + augment /oc-stp:stp/oc-stp-ext:pvst/oc-stp-ext:vlan/oc-stp-ext:interfaces/oc-stp-ext:interface/oc-stp-ext:state { + uses vlan-interface-extra-state-field; + } + + augment /oc-stp:stp/oc-stp-ext:pvst/oc-stp-ext:vlan/oc-stp-ext:interfaces/oc-stp-ext:interface/oc-stp-ext:state/oc-stp-ext:counters { + uses vlan-interface-extra-counters; + } + + augment /oc-stp:stp/oc-stp:interfaces/oc-stp:interface/oc-stp:config { + uses stp-interface-common-ext-config; + } + + augment /oc-stp:stp/oc-stp:interfaces/oc-stp:interface/oc-stp:state { + leaf bpdu-guard-shutdown { + type boolean; + description + "Port disabled due to bpdu-guard"; + } + uses stp-interface-common-ext-config; + } + + + deviation /oc-stp:stp/oc-stp:global/oc-stp:config/oc-stp:bridge-assurance { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:state/oc-stp:bridge-assurance { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:config/oc-stp:etherchannel-misconfig-guard { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:state/oc-stp:etherchannel-misconfig-guard { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:config/oc-stp:bpduguard-timeout-recovery { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:state/oc-stp:bpduguard-timeout-recovery { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:config/oc-stp-ext:hold-count { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:config/oc-stp:loop-guard { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:state/oc-stp:loop-guard { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:config/oc-stp:bpdu-guard { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:global/oc-stp:state/oc-stp:bpdu-guard { + deviate not-supported; + } + + deviation /oc-stp:stp/oc-stp-ext:pvst/oc-stp-ext:vlan/oc-stp-ext:config/oc-stp-ext:hold-count { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp-ext:pvst/oc-stp-ext:vlan/oc-stp-ext:state/oc-stp-ext:hold-count { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:rapid-pvst/oc-stp:vlan/oc-stp:config/oc-stp:hold-count { + deviate not-supported; + } + deviation /oc-stp:stp/oc-stp:rapid-pvst/oc-stp:vlan/oc-stp:state/oc-stp:hold-count { + deviate not-supported; + } + + deviation /oc-stp:stp/oc-stp:rstp { + deviate not-supported; + } + + deviation /oc-stp:stp/oc-stp:mstp { + deviate not-supported; + } +} diff --git a/models/yang/openconfig-spanning-tree.yang b/models/yang/openconfig-spanning-tree.yang new file mode 100755 index 0000000000..9e72331046 --- /dev/null +++ b/models/yang/openconfig-spanning-tree.yang @@ -0,0 +1,836 @@ +module openconfig-spanning-tree { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/spanning-tree"; + + prefix "oc-stp"; + + import openconfig-spanning-tree-types { prefix oc-stp-types; } + import openconfig-interfaces { prefix oc-if; } + import openconfig-types { prefix oc-types; } + import openconfig-vlan-types { prefix oc-vlan-types; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-extensions { prefix oc-ext; } + + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + www.openconfig.net"; + + description + "This module defines configuration and operational state data + for the spanning tree protocol."; + + oc-ext:openconfig-version "0.3.0"; + + revision "2014-03-14" { + description + "Remove the time-since-last-topology-change leaf and + replace it with a timestamp of last topology change."; + reference "0.3.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.2.1"; + } + + revision "2017-07-14" { + description + "Migrated to OpenConfig types; fixed missing applied state + in rapid-pvst"; + reference "0.2.0"; + } + + revision "2016-10-03" { + description + "Initial public revision"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // identity statements + + + // grouping statements + + grouping stp-interfaces-state { + description + "Grouping of STP operational data for bridge port"; + + leaf port-num { + type uint16; + description + "The port number of the bridge port"; + reference "RFC4188 BRIDGE-MIB dot1dStpPort"; + } + + leaf role { + type identityref { + base oc-stp-types:STP_PORT_ROLE; + } + description + "The current role of the bridge port"; + reference + "IEEE8021-MSTP-MIB ieee8021MstpPortRole"; + } + + leaf port-state { + type identityref { + base oc-stp-types:STP_PORT_STATE; + } + description + "The current state of the bridge port"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortState"; + } + + leaf designated-root-priority { + type oc-stp-types:stp-bridge-priority-type; + description + "The bridge priority of the bridge recorded as the + root in the configuration BPDUs transmitted by the designated + bridge for the segment to which the port is attached"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedRoot"; + } + + leaf designated-root-address { + type oc-yang:mac-address; + description + "The bridge address of the bridge recorded as the + root in the configuration BPDUs transmitted by the designated + bridge for the segment to which the port is attached"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedRoot"; + } + + leaf designated-cost { + type uint32; + description + "The path cost of the Designated Port of the + segment connected to this port"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedCost"; + } + + leaf designated-bridge-priority { + type oc-stp-types:stp-bridge-priority-type; + description + "The bridge priority of the bridge that this port considers + to be the designated bridge for this port's segment."; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedBridge"; + } + + leaf designated-bridge-address { + type oc-yang:mac-address; + description + "The bridge address of the bridge that this port considers + to be the designated bridge for this port's segment."; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedBridge"; + } + + leaf designated-port-priority { + type oc-stp-types:stp-port-priority-type; + description + "The Port priority of the port on the Designated + Bridge for this port's segment, two octet string"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedPort"; + } + + leaf designated-port-num { + type uint16; + description + "The Port number of the port on the Designated + Bridge for this port's segment, two octet string"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortDesignatedPort"; + } + + leaf forward-transisitions { + type oc-yang:counter64; + description + "The number of times this port has transitioned + from the Learning state to the Forwarding state"; + reference "RFC4188 BRIDGE-MIB dot1dStpPortForwardTransitions"; + } + + container counters { + description + "The BPDU packet transmition statistics"; + + leaf bpdu-sent { + type oc-yang:counter64; + description + "The number of BPDU packet sent"; + } + + leaf bpdu-received { + type oc-yang:counter64; + description + "The number of BPDU packet received"; + } + } + } + + grouping stp-interfaces-config { + description + "Grouping of STP configuration for bridge port"; + + leaf name { + type oc-if:base-interface-ref; + description + "Reference to the STP ethernet interface"; + } + + leaf cost { + type uint32 { + range 1..200000000; + } + description + "The port's contribution, when it is the Root Port, + to the Root Path Cost for the Bridge"; + reference + "IEEE 802.1D 17.13.11 PortPathCost"; + } + + leaf port-priority { + type oc-stp-types:stp-port-priority-type; + description + "The manageable component of the Port Identifier, + also known as the Port Priority"; + reference + "IEEE 802.1D 17.13.10 Port Identifier Priority"; + } + } + + grouping stp-interfaces-top { + description + "Grouping of STP configuration and operation data for + bridge port"; + + container interfaces { + description + "Enclosing container for the list of interface references"; + + list interface { + key "name"; + description + "List of interfaces on which STP is enable"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for STP on each interface"; + + uses stp-interfaces-config; + } + + container state { + + config false; + + description + "Operational state data for STP on each interface"; + + uses stp-interfaces-config; + uses stp-interfaces-state; + } + } + } + } + + grouping bridge-priority-config { + description + "Grouping for bridge priority"; + + leaf bridge-priority { + type oc-stp-types:stp-bridge-priority-type; + description + "The manageable component of the Bridge Identifier"; + reference + "IEEE 802.1D 17.13.7 Bridge Identifier Priority"; + } + } + + grouping stp-common-state { + description + "Grouping for common STP operation data"; + + leaf bridge-address { + type oc-yang:mac-address; + description + "A unique 48-bit Universally Administered MAC Address + assigned to the bridge"; + reference + "IEEE 802.1D 7.12.5 Unique identification of a bridge"; + } + + leaf designated-root-priority { + type oc-stp-types:stp-bridge-priority-type; + description + "The bridge priority of the root of the spanning + tree, as determined by the Spanning Tree Protocol, + as executed by this node"; + reference + "RFC4188 BRIDGE-MIB dot1dStpDesignatedRoot"; + } + + leaf designated-root-address { + type oc-yang:mac-address; + description + "The bridge address of the root of the spanning + tree, as determined by the Spanning Tree Protocol, + as executed by this node"; + reference + "RFC4188 BRIDGE-MIB dot1dStpDesignatedRoot"; + } + + leaf root-port { + type uint16; + description + "The port number of the port which offers the lowest + cost path from this bridge to the root bridge"; + reference + "RFC4188 BRIDGE-MIB dot1dStpRootPort"; + } + + leaf root-cost { + type uint32; + description + "The cost of the path to the root as seen from this bridge"; + reference + "RFC4188 BRIDGE-MIB dot1dStpRootCost"; + } + + leaf hold-time { + type uint8; + description + "This time value determines the interval length + during which no more than two Configuration bridge + PDUs shall be transmitted by this node"; + reference + "RFC4188 BRIDGE-MIB dot1dStpHoldTime"; + } + + leaf topology-changes { + type oc-yang:counter64; + description + "The total number of topology changes detected by + this bridge since the management entity was last + reset or initialized"; + reference + "RFC4188 BRIDGE-MIB dot1dStpTopChanges"; + } + + leaf last-topology-change { + type oc-types:timeticks64; + description + "The time at which the last topology change was + detected by the bridge entity. The value is + expressed relative to the Unix Epoch (Jan 1, 1970 + 00:00:00 UTC)."; + } + } + + grouping stp-timer-config { + description + "Grouping for common STP parameters"; + + leaf hello-time { + type uint8 { + range 1..10; + } + units "seconds"; + description + "The interval between periodic transmissions of + configuration messages by designated ports"; + reference + "IEEE 802.1D 17.13.6 Bridge Hello Time"; + } + + leaf max-age { + type uint8 { + range 6..40; + } + units "seconds"; + description + "The maximum age of the information transmitted by the + bridge when it is the root bridge"; + reference + "IEEE 802.1D 17.13.8 Bridge Max Age"; + } + + leaf forwarding-delay { + type uint8 { + range 4..30; + } + units "seconds"; + description + "The delay used by STP bridges to transition root and + designated ports to forwarding"; + reference + "IEEE 802.1D 17.13.5 Bridge Forward Delay"; + } + + leaf hold-count { + type uint8 { + range 1..10; + } + default 6; + description + "the maximum number of BPDUs per second that the + switch can send from an interface"; + reference + "IEEE 802.1D 17.13.12 Transmit Hold Count"; + } + } + + grouping stp-rapid-pvst-config { + description + "Configuration parameters relating to rapid PVST"; + + leaf vlan-id { + type oc-vlan-types:vlan-id; + description + "Interface VLAN ID"; + } + } + + grouping stp-rapid-pvst-top { + description + "Top grouping for rapid per vlan spanning tree configuration + and operation data"; + + list vlan { + key "vlan-id"; + description + "List of the vlans"; + + leaf vlan-id { + type leafref { + path "../config/vlan-id"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for each vlan"; + + uses stp-rapid-pvst-config; + uses stp-timer-config; + uses bridge-priority-config; + } + + container state { + config false; + description + "Operational data for each vlan"; + + uses stp-rapid-pvst-config; + uses stp-timer-config; + uses bridge-priority-config; + uses stp-common-state; + } + + uses stp-interfaces-top; + } + } + + grouping mst-instance-config { + description + "Grouping for mstp instance configuration"; + + leaf mst-id { + type uint16 { + range "1..4094"; + } + description + "In an MSTP Bridge, an MSTID, i.e., a value used to identify + a spanning tree (or MST) instance."; + reference + "IEEE8021-TC-MIB IEEE8021MstIdentifier"; + } + + leaf-list vlan { + type union { + type oc-vlan-types:vlan-id; + type oc-vlan-types:vlan-range; + } + description + "list of vlans mapped to the MST instance"; + } + } + + grouping mst-instance-top { + description + "Top level grouping for mstp instances"; + + list mst-instance { + key "mst-id"; + description + "List of the mstp instances"; + + leaf mst-id { + type leafref { + path "../config/mst-id"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for MSTP instance"; + + uses mst-instance-config; + uses bridge-priority-config; + } + + container state { + config false; + + description + "Operational data for MSTP instance"; + + uses mst-instance-config; + uses bridge-priority-config; + uses stp-common-state; + } + + uses stp-interfaces-top; + } + } + + grouping mstp-config { + description + "Grouping for MSTP configuration data"; + + leaf name { + type string { + length "1..32"; + } + description + "The Configuration Name in the MST Configuration Identifier"; + reference + "IEEE 802.1Q 13.8 MST Configuration Identifier (MCID)"; + } + + leaf revision { + type uint32; + description + "The Revision Level in the MST Configuration Identifier"; + reference + "IEEE 802.1Q 13.8 MST Configuration Identifier"; + } + + leaf max-hop { + type uint8 { + range 1..255; + } + description + "The max hop determines the number of bridges in an MST + region that a BPDU can traverse before it is discarded"; + reference + "IEEE 802.1Q 13.26.4 BridgeTimes"; + } + + uses stp-timer-config; + } + + grouping mstp-state { + description + "Operational state data for MSTP"; + } + + grouping stp-mstp-top { + description + "Top grouping for MSTP configuration and operation data"; + + container config { + description + "Configuration data for MSTP"; + + uses mstp-config; + } + + container state { + config false; + + description + "Operational data for MSTP"; + + uses mstp-config; + uses mstp-state; + } + + container mst-instances { + description + "Configuration and operation data for MSTP instances"; + + uses mst-instance-top; + } + } + + grouping stp-rstp-top { + description + "Top grouping for RSTP configuration and operation data"; + + container config { + description + "Configuration data for RSTP"; + + uses stp-timer-config; + uses bridge-priority-config; + } + + container state { + config false; + + description + "Operational state data for RSTP"; + + uses stp-timer-config; + uses bridge-priority-config; + uses stp-common-state; + } + + uses stp-interfaces-top; + } + + grouping stp-interface-common-config { + description + "Configuration data for interface specific STP features"; + + leaf name { + type oc-if:base-interface-ref; + description + "Reference to the STP Ethernet interface"; + } + + leaf edge-port { + type identityref { + base oc-stp-types:STP_EDGE_PORT; + } + description + "Configure the edge port state"; + } + + leaf link-type { + type oc-stp-types:stp-link-type; + description + "specifies the interface's link type"; + } + + leaf guard { + type oc-stp-types:stp-guard-type; + description + "Enable root guard or loop guard"; + } + + uses stp-bpdu-config; + + } + + grouping stp-interface-common-state { + description + "Operational state data for STP on interfaces"; + } + + grouping stp-interface-common-top { + description + "Top-level grouping for interface specific STP features"; + + list interface { + key "name"; + description + "List of interfaces on which STP is enable"; + + leaf name { + type leafref { + path "../config/name"; + } + description + "Reference to the list key"; + } + + container config { + description + "Configuration data for STP on each bridge port"; + + uses stp-interface-common-config; + } + + container state { + + config false; + + description + "Operational state data for STP on each bridge port"; + + uses stp-interface-common-config; + uses stp-interface-common-state; + } + } + } + + grouping stp-bpdu-config { + description + "Grouping for STP BPDU configuration"; + + leaf bpdu-guard { + type boolean; + description + "Enable edge port BPDU guard"; + } + + leaf bpdu-filter { + type boolean; + description + "Enable edge port BPDU filter"; + } + } + + grouping stp-global-config { + description + "Global spanning tree configuration"; + + leaf-list enabled-protocol { + type identityref { + base oc-stp-types:STP_PROTOCOL; + } + description + "List of the spanning tree protocols enabled on the + device"; + } + + leaf bridge-assurance { + type boolean; + description + "Enable bridge assurance to protect against unidirectional + link failure"; + } + + leaf etherchannel-misconfig-guard { + type boolean; + description + "EtherChannel guard detects a misconfigured EtherChannel + when interfaces on the switch are configured as an + EtherChannel while interfaces on the other device are not + or when not all the interfaces on the other device are in + the same EtherChannel."; + } + + leaf bpduguard-timeout-recovery { + type uint8; + units "seconds"; + description + "Amount of time, in seconds, the interface receiving BPDUs + is disabled. Once the timeout expires, the interface is + brought back into service."; + } + + leaf loop-guard { + type boolean; + description + "The loop guard default setting for the bridge"; + } + + uses stp-bpdu-config; + + } + + grouping stp-global-state { + description + "Global operational state for STP"; + } + + grouping stp-global-base { + description + "Grouping for global spanning tree data"; + + container config { + description + "Global spanning tree configuration"; + uses stp-global-config; + } + + container state { + config false; + + description + "Global spanning tree state"; + uses stp-global-config; + uses stp-global-state; + } + } + + grouping stp-top { + description + "Top-level grouping for spanning-tree model"; + + container stp { + description + "Top-level container for spanning tree configuration and + state data"; + + container global { + description + "Global configuration and state data"; + + uses stp-global-base; + } + + container rstp { + + description + "Rapid Spanning-tree protocol configuration and operation + data"; + + uses stp-rstp-top; + } + + container mstp { + description + "Multi Spanning-tree protocol configuration and operation + data"; + + uses stp-mstp-top; + } + + container rapid-pvst { + description + "Rapid per vlan Spanning-tree protocol configuration and + operational data"; + + uses stp-rapid-pvst-top; + } + + container interfaces { + description + "Enclosing container for the list of interface references"; + + uses stp-interface-common-top; + } + } + } + + // data definition statements + + uses stp-top; + +} diff --git a/models/yang/openconfig-system.yang b/models/yang/openconfig-system.yang new file mode 100644 index 0000000000..fd05a3f771 --- /dev/null +++ b/models/yang/openconfig-system.yang @@ -0,0 +1,997 @@ +module openconfig-system { + + yang-version "1"; + + // namespace + namespace "http://openconfig.net/yang/system"; + + prefix "oc-sys"; + + // import some basic types + import openconfig-inet-types { prefix oc-inet; } + import openconfig-yang-types { prefix oc-yang; } + import openconfig-types { prefix oc-types; } + import openconfig-extensions { prefix oc-ext; } + import openconfig-aaa { prefix oc-aaa; } + import openconfig-system-logging { prefix oc-log; } + import openconfig-system-management { prefix oc-sys-mgmt; } + import openconfig-system-terminal { prefix oc-sys-term; } + import openconfig-procmon { prefix oc-proc; } + import openconfig-alarms { prefix oc-alarms; } + import openconfig-messages { prefix oc-messages; } + + // meta + organization "OpenConfig working group"; + + contact + "OpenConfig working group + netopenconfig@googlegroups.com"; + + description + "Model for managing system-wide services and functions on + network devices. + + Portions of this code were derived from IETF RFC 7317. + Please reproduce this note if possible. + + IETF code is subject to the following copyright and license: + Copyright (c) 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)."; + + oc-ext:openconfig-version "0.7.0"; + + revision "2019-01-29" { + description + "Add messages module to the system model"; + reference "0.7.0"; + } + + revision "2018-11-21" { + description + "Add OpenConfig module metadata extensions."; + reference "0.6.1"; + } + + revision "2018-07-17" { + description + "Add gRPC server data"; + reference "0.6.0"; + } + + revision "2018-01-21" { + description + "Add cpu utilization data"; + reference "0.5.0"; + } + + revision "2017-12-15" { + description + "Add alarms to the system model"; + reference "0.4.0"; + } + + revision "2017-09-18" { + description + "Updated to use OpenConfig types modules"; + reference "0.3.0"; + } + + revision "2017-07-06" { + description + "Move to oc-inet types, add IETF attribution, add RADIUS + counters, changed password leaf names to indicate hashed"; + reference "0.2.0"; + } + + revision "2017-01-29" { + description + "Initial public release"; + reference "0.1.0"; + } + + // OpenConfig specific extensions for module metadata. + oc-ext:regexp-posix; + oc-ext:catalog-organization "openconfig"; + oc-ext:origin "openconfig"; + + // extension statements + + // feature statements + + // identity statements + + identity NTP_AUTH_TYPE { + description + "Base identity for encryption schemes supported for NTP + authentication keys"; + } + + identity NTP_AUTH_MD5 { + base NTP_AUTH_TYPE; + description + "MD5 encryption method"; + } + + // typedef statements + + typedef timezone-name-type { + type string; + description + "A time zone name as used by the Time Zone Database, + sometimes referred to as the 'Olson Database'. + + The exact set of valid values is an implementation-specific + matter. Client discovery of the exact set of time zone names + for a particular server is out of scope."; + reference + "BCP 175: Procedures for Maintaining the Time Zone Database"; + } + + // grouping statements + + grouping system-clock-config { + description + "Configuration data for system-wide clock configuration"; + + leaf timezone-name { + type timezone-name-type; + description + "The TZ database name to use for the system, such + as 'Europe/Stockholm'."; + reference "IANA Time Zone Database + http://www.iana.org/time-zones"; + } + } + + grouping system-clock-state { + description + "Operational state data for system-wide clock configuration"; + } + + grouping system-clock-top { + description + "Top-level grouping for system-wide clock configuration"; + + container clock { + description + "Top-level container for clock configuration data"; + + container config { + description + "Configuration data for system clock"; + + uses system-clock-config; + } + + container state { + + config false; + + description + "Operational state data for system clock"; + + uses system-clock-config; + uses system-clock-state; + } + } + } + + grouping system-global-config { + description "system-wide configuration parameters"; + + leaf hostname { + type oc-inet:domain-name; + description + "The hostname of the device -- should be a single domain + label, without the domain."; + } + + leaf domain-name { + type oc-inet:domain-name; + description + "Specifies the domain name used to form fully qualified name + for unqualified hostnames."; + } + + leaf login-banner { + type string; + description + "The console login message displayed before the login prompt, + i.e., before a user logs into the system."; + } + + leaf motd-banner { + type string; + description + "The console message displayed after a user logs into the + system. They system may append additional standard + information such as the current system date and time, uptime, + last login timestamp, etc."; + } + } + + grouping system-global-state { + description + "Global operational state data for the system"; + + leaf current-datetime { + type oc-yang:date-and-time; + description + "The current system date and time."; + } + + leaf boot-time { + type oc-types:timeticks64; + description + "This timestamp indicates the time that the system was last + restarted. The value is the timestamp in seconds relative + to the Unix Epoch (Jan 1, 1970 00:00:00 UTC)."; + } + + } + + grouping system-dns-config { + description "DNS / resolver related configuration data"; + + leaf-list search { + type oc-inet:domain-name; + ordered-by user; + description + "An ordered list of domains to search when resolving + a host name."; + } + } + + grouping system-dns-state { + description + "Operational state data for system DNS resolver"; + + } + + grouping system-dns-servers-config { + description + "Configuration data for DNS resolvers"; + + //RFC 7317 includes a single-value choice statement to for + //TCP and UDP transport. This has been removed since it the + //transport protocol is not generally available as an options + //on target devices. It may be added back if and when needed. + + leaf address { + type oc-inet:ip-address; + description + "The address of the DNS server, can be either IPv4 + or IPv6."; + } + + leaf port { + type oc-inet:port-number; + default 53; + description + "The port number of the DNS server."; + } + + //RFC 7317 includes resolver timeout and attempts options. These + //have been omitted as they are not available on many targets. If + //and when they are required, they may be added back in. + } + + grouping system-dns-static-config { + description + "Configuration data for static host entries"; + + leaf hostname { + type string; + description + "Hostname for the static DNS entry"; + } + + leaf-list alias { + type string; + description + "Additional aliases for the hostname"; + } + + leaf-list ipv4-address { + type oc-inet:ipv4-address; + description + "List of IPv4 addressses for the host entry"; + } + + leaf-list ipv6-address { + type oc-inet:ipv6-address; + description + "List of IPv6 addresses for the host entry"; + } + } + + grouping system-dns-static-state { + description + "Operational state data for static host entries"; + } + + grouping system-dns-static-top { + description + "Top-level grouping for static DNS host entries"; + + container host-entries { + description + "Enclosing container for list of static host entries"; + + list host-entry { + key "hostname"; + description + "List of static host entries"; + + leaf hostname { + type leafref { + path "../config/hostname"; + } + description + "Reference to the hostname list key"; + } + + container config { + description + "Configuration data for static host entries"; + + uses system-dns-static-config; + } + + container state { + + config false; + + description + "Operational state data for static host entries"; + + uses system-dns-static-config; + uses system-dns-static-state; + } + } + } + } + + grouping system-dns-servers-state { + description + "Operational state data for DNS resolvers"; + + } + + grouping system-dns-servers-top { + description + "Top-level grouping for the list of DNS resolvers."; + + container servers { + description + "Enclosing container for DNS resolver list"; + + list server { + key "address"; + ordered-by user; + description + "List of the DNS servers that the resolver should query. + + When the resolver is invoked by a calling application, it + sends the query to the first name server in this list. If + no response has been received within 'timeout' seconds, + the resolver continues with the next server in the list. + If no response is received from any server, the resolver + continues with the first server again. When the resolver + has traversed the list 'attempts' times without receiving + any response, it gives up and returns an error to the + calling application. + + Implementations MAY limit the number of entries in this + list."; + + leaf address { + type leafref { + path "../config/address"; + } + description + "References the configured address of the DNS server"; + } + + container config { + description + "Configuration data for each DNS resolver"; + + uses system-dns-servers-config; + } + + container state { + + config false; + + description + "Operational state data for each DNS resolver"; + + uses system-dns-servers-config; + uses system-dns-servers-state; + } + + } + } + } + + grouping system-dns-top { + description + "Top-level grouping for DNS / resolver config and operational + state data"; + + container dns { + description + "Enclosing container for DNS resolver data"; + + container config { + description + "Configuration data for the DNS resolver"; + + uses system-dns-config; + + } + + container state { + + config false; + + description + "Operational state data for the DNS resolver"; + + uses system-dns-config; + uses system-dns-state; + + } + + uses system-dns-servers-top; + uses system-dns-static-top; + } + } + + grouping system-ntp-server-config { + description + "Configuration data for NTP servers"; + + leaf address { + type oc-inet:host; + description + "The address or hostname of the NTP server."; + } + + leaf port { + type oc-inet:port-number; + default 123; + description + "The port number of the NTP server."; + } + + leaf version { + type uint8 { + range 1..4; + } + default 4; + description + "Version number to put in outgoing NTP packets"; + } + + leaf association-type { + type enumeration { + enum SERVER { + description + "Use client association mode. This device + will not provide synchronization to the + configured NTP server."; + } + enum PEER { + description + "Use symmetric active association mode. + This device may provide synchronization + to the configured NTP server."; + } + enum POOL { + description + "Use client association mode with one or + more of the NTP servers found by DNS + resolution of the domain name given by + the 'address' leaf. This device will not + provide synchronization to the servers."; + } + } + default SERVER; + description + "The desired association type for this NTP server."; + } + leaf iburst { + type boolean; + default false; + description + "Indicates whether this server should enable burst + synchronization or not."; + } + leaf prefer { + type boolean; + default false; + description + "Indicates whether this server should be preferred + or not."; + } + } + + grouping system-ntp-server-state { + description + "Operational state data for NTP servers"; + + leaf stratum { + type uint8; + description + "Indicates the level of the server in the NTP hierarchy. As + stratum number increases, the accuracy is degraded. Primary + servers are stratum while a maximum value of 16 indicates + unsynchronized. The values have the following specific + semantics: + + | 0 | unspecified or invalid + | 1 | primary server (e.g., equipped with a GPS receiver) + | 2-15 | secondary server (via NTP) + | 16 | unsynchronized + | 17-255 | reserved"; + reference + "RFC 5905 - Network Time Protocol Version 4: Protocol and + Algorithms Specification"; + } + + leaf root-delay { + type uint32; + // TODO: reconsider units for these values -- the spec defines + // rootdelay and rootdisperson as 2 16-bit integers for seconds + // and fractional seconds, respectively. This gives a + // precision of ~15 us (2^-16). Using milliseconds here based + // on what implementations typically provide and likely lack + // of utility for less than millisecond precision with NTP + // time sync. + units "milliseconds"; + description + "The round-trip delay to the server, in milliseconds."; + reference + "RFC 5905 - Network Time Protocol Version 4: Protocol and + Algorithms Specification"; + } + + leaf root-dispersion { + type uint64; + units "milliseconds"; + description + "Dispersion (epsilon) represents the maximum error inherent + in the measurement"; + reference + "RFC 5905 - Network Time Protocol Version 4: Protocol and + Algorithms Specification"; + } + + leaf offset { + type uint64; + units "milliseconds"; + description + "Estimate of the current time offset from the peer. This is + the time difference between the local and reference clock."; + } + + leaf poll-interval { + type uint32; + units "seconds"; + description + "Polling interval of the peer"; + } + } + + grouping system-ntp-server-top { + description + "Top-level grouping for the list of NTP servers"; + + container servers { + description + "Enclosing container for the list of NTP servers"; + + list server { + key "address"; + description + "List of NTP servers to use for system clock + synchronization. If '/system/ntp/enabled' + is 'true', then the system will attempt to + contact and utilize the specified NTP servers."; + + leaf address { + type leafref { + path "../config/address"; + } + description + "References the configured address or hostname of the + NTP server."; + } + + container config { + description + "Configuration data for an NTP server."; + + uses system-ntp-server-config; + } + + container state { + + config false; + + description + "Operational state data for an NTP server."; + + uses system-ntp-server-config; + uses system-ntp-server-state; + } + + } + } + } + + grouping system-ntp-auth-keys-config { + description + "Configuration data "; + + leaf key-id { + type uint16; + description + "Integer identifier used by the client and server to + designate a secret key. The client and server must use + the same key id."; + } + + leaf key-type { + type identityref { + base NTP_AUTH_TYPE; + } + description + "Encryption type used for the NTP authentication key"; + } + + leaf key-value { + type string; + description + "NTP authentication key value"; + } + } + + grouping system-ntp-auth-keys-state { + description + "Operational state data for NTP auth key data"; + } + + grouping system-ntp-auth-keys-top { + description + "Top-level grouping for NTP auth key data"; + + container ntp-keys { + description + "Enclosing container for list of NTP authentication keys"; + + list ntp-key { + key "key-id"; + description + "List of NTP authentication keys"; + + leaf key-id { + type leafref { + path "../config/key-id"; + } + description + "Reference to auth key-id list key"; + } + + container config { + description + "Configuration data for NTP auth keys"; + + uses system-ntp-auth-keys-config; + } + + container state { + + config false; + + description + "Operational state data for NTP auth keys"; + + uses system-ntp-auth-keys-config; + uses system-ntp-auth-keys-state; + } + } + } + } + + grouping system-ntp-config { + description + "Configuration data for system-wide NTP operation."; + + leaf enabled { + type boolean; + default false; + description + "Enables the NTP protocol and indicates that the system should + attempt to synchronize the system clock with an NTP server + from the servers defined in the 'ntp/server' list."; + } + + leaf ntp-source-address { + type oc-inet:ip-address; + description + "Source address to use on outgoing NTP packets"; + } + + leaf enable-ntp-auth { + type boolean; + default false; + description + "Enable or disable NTP authentication -- when enabled, the + system will only use packets containing a trusted + authentication key to synchronize the time."; + } + } + + grouping system-ntp-state { + description + "Operational state data for system-wide NTP operation."; + + leaf auth-mismatch { + type oc-yang:counter64; + description + "Count of the number of NTP packets received that were not + processed due to authentication mismatch."; + } + } + + grouping system-ntp-top { + description + "Top-level grouping for configuration and state data for NTP"; + + container ntp { + description + "Top-level container for NTP configuration and state"; + + container config { + description + "Configuration data for NTP client."; + + uses system-ntp-config; + } + + container state { + config false; + description + "Operational state data for NTP services."; + + uses system-ntp-config; + uses system-ntp-state; + } + uses system-ntp-auth-keys-top; + uses system-ntp-server-top; + } + } + + grouping system-memory-config { + description + "Configuration data for system memory"; + } + + grouping system-memory-state { + description + "Operational state data for system memory"; + + leaf physical { + type uint64; + units bytes; + // TODO: consider making units in megabytes + description + "Reports the total physical memory available on the + system."; + } + + leaf reserved { + type uint64; + units bytes; + description + "Memory reserved for system use"; + } + } + + grouping system-memory-top { + description + "Top-level grouping for system memory data definitions"; + + container memory { + description + "Top-level container for system memory data"; + + container config { + description + "Configuration data for system memory"; + + uses system-memory-config; + } + + container state { + config false; + description + "Operational state data for system memory"; + + uses system-memory-config; + uses system-memory-state; + } + } + } + + grouping system-cpu-state { + description + "Operational state data for the system CPU(s)"; + + leaf index { + type union { + type enumeration { + enum ALL { + description + "Index value indicating all CPUs in the system"; + } + } + type uint32; + } + description + "The CPU index for each processor core on the system. On a + single-core system, the index should be zero. The ALL + index signifies an aggregation of the CPU utilization + statistics over all cores in the system."; + } + + container total { + description + "Total CPU utilization."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container user { + description + "Percentage of CPU time spent running in user space."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container kernel { + description + "Percentage of CPU time spent running in kernel space."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container nice { + description + "Percentage of CPU time spent running low-priority (niced) + user processes."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container idle { + description + "Percentage of CPU time spent idle."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container wait { + description + "Percentage of CPU time spent waiting for I/O."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container hardware-interrupt { + description + "Percentage of CPU time spent servicing hardware interrupts."; + + uses oc-types:avg-min-max-instant-stats-pct; + } + + container software-interrupt { + description + "Percentage of CPU time spent servicing software interrupts"; + + uses oc-types:avg-min-max-instant-stats-pct; + } + } + + grouping system-cpu-top { + description + "Top-level grouping for system CPU data"; + + container cpus { + config false; + description + "Enclosing container for the list of CPU cores on the + system"; + + list cpu { + key "index"; + description + "List of CPU cores on the system (including logical CPUs + on hyperthreaded systems), keyed by either a numerical + index, or the ALL value for an entry representing the + aggregation across all CPUs."; + + leaf index { + type leafref { + path "../state/index"; + } + description + "Reference to list key"; + } + + container state { + + description + "Operational state data for the system CPU(s)"; + + uses system-cpu-state; + } + } + } + } + + grouping system-top { + description + "Top level system data containers"; + + container system { + description + "Enclosing container for system-related configuration and + operational state data"; + + container config { + description "Global configuration data for the system"; + + uses system-global-config; + + } + + container state { + config false; + description "Global operational state data for the system"; + + uses system-global-config; + uses system-global-state; + } + + uses system-clock-top; + uses system-dns-top; + uses system-ntp-top; + uses oc-sys-mgmt:system-grpc-server-top; + uses oc-sys-term:system-ssh-server-top; + uses oc-sys-term:system-telnet-server-top; + uses oc-log:logging-top; + uses oc-aaa:aaa-top; + uses system-memory-top; + uses system-cpu-top; + uses oc-proc:procmon-processes-top; + uses oc-alarms:alarms-top; + uses oc-messages:messages-top; + } + } + + // data definition statements + + uses system-top; + +} 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..5fdf0dbe3c --- /dev/null +++ b/models/yang/sonic/common/sonic-common.yang @@ -0,0 +1,50 @@ +module sonic-common { + namespace "http://github.com/Azure/sonic-common"; + prefix scommon; + + import ietf-yang-types { + prefix yang; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC Common"; + + 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; + } + } + + typedef oper-status { + type enumeration { + enum up; + enum down; + } + } + + container operation { + leaf operation { + type string; + } + } +} diff --git a/models/yang/sonic/common/sonic-extension.yang b/models/yang/sonic/common/sonic-extension.yang new file mode 100644 index 0000000000..937c7adb34 --- /dev/null +++ b/models/yang/sonic/common/sonic-extension.yang @@ -0,0 +1,56 @@ + +module sonic-extension { + namespace "http://github.com/Azure/sonic-extension"; + prefix sonic-ext; + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC Extension"; + + revision 2019-09-18 { + description + "Initial revision."; + } + + extension db-name { + description + "DB name, e.g. APPL_DB, CONFIG_DB"; + argument "value"; + } + + extension key-delim { + description + "Key delimeter, e.g. - |, :"; + argument "value"; + } + + extension key-pattern { + description + "Key pattern, e.g. - ACL_RULE|{aclname}|{rulename}"; + argument "value"; + } + + extension map-list { + description + "If it is a map list"; + argument "value"; + } + + extension map-leaf { + description + "Map leaf names"; + argument "value"; + } + + extension custom-validation { + description + "Extension for custom validation. + Platform specific validation can be implemented using custom validation."; + argument "handler"; + } +} diff --git a/models/yang/sonic/platform/accton_as5712/sonic-acl-deviation.yang b/models/yang/sonic/platform/accton_as5712/sonic-acl-deviation.yang new file mode 100644 index 0000000000..7c14d38b45 --- /dev/null +++ b/models/yang/sonic/platform/accton_as5712/sonic-acl-deviation.yang @@ -0,0 +1,35 @@ +module sonic-acl-deviation { + namespace "http://github.com/Azure/sonic-acl-deviation"; + prefix acld; + yang-version 1.1; + + import sonic-acl { + prefix sacl; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONiC ACL Deviations"; + + revision 2019-11-11 { + description + "Initial revision."; + } + + deviation /sacl:sonic-acl/sacl:ACL_TABLE/sacl:ACL_TABLE_LIST { + deviate add { + max-elements 8; + } + } + + deviation /sacl:sonic-acl/sacl:ACL_RULE/sacl:ACL_RULE_LIST { + deviate add { + max-elements 256; + } + } +} diff --git a/models/yang/sonic/platform/quanta_ix8/sonic-acl-deviation.yang b/models/yang/sonic/platform/quanta_ix8/sonic-acl-deviation.yang new file mode 100644 index 0000000000..b3de60eff1 --- /dev/null +++ b/models/yang/sonic/platform/quanta_ix8/sonic-acl-deviation.yang @@ -0,0 +1,35 @@ +module sonic-acl-deviation { + namespace "http://github.com/Azure/sonic-acl-deviation"; + prefix acld; + yang-version 1.1; + + import sonic-acl { + prefix sacl; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONiC ACL Deviations"; + + revision 2019-11-11 { + description + "Initial revision."; + } + + deviation /sacl:sonic-acl/sacl:ACL_TABLE/sacl:ACL_TABLE_LIST { + deviate add { + max-elements 3; + } + } + + deviation /sacl:sonic-acl/sacl:ACL_RULE/sacl:ACL_RULE_LIST { + deviate add { + max-elements 768; + } + } +} diff --git a/models/yang/sonic/sonic-acl.yang b/models/yang/sonic/sonic-acl.yang new file mode 100644 index 0000000000..776afcb751 --- /dev/null +++ b/models/yang/sonic/sonic-acl.yang @@ -0,0 +1,227 @@ +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-extension { + prefix sonic-ext; + } + + 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"; + sonic-ext:custom-validation ValidateMaxAclTable; + + 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 { + sonic-ext:custom-validation ValidateAclRuleIPAddress; + 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-fdb.yang b/models/yang/sonic/sonic-fdb.yang new file mode 100644 index 0000000000..ebabe9eea8 --- /dev/null +++ b/models/yang/sonic/sonic-fdb.yang @@ -0,0 +1,59 @@ +module sonic-fdb { + namespace "http://github.com/Azure/sonic-fdb"; + prefix sfdb; + + import ietf-yang-types { + prefix yang; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC FDB"; + + revision 2019-10-10 { + description + "Initial revision."; + } + + container sonic-fdb { + + container FDB_TABLE { + + list FDB_TABLE_LIST { + key "vlan mac-address"; + + leaf vlan { + 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 mac-address { + type yang:mac-address; + description + "MAC address for the dynamic or static MAC table entry"; + } + + leaf port { + type string; + description + "Reference to the base and/or subinterface for the MAC table entry"; + } + + leaf type { + type string; + description + "Statically or dynamically programmed MAC table entry"; + } + } + } + } +} 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-mclag.yang b/models/yang/sonic/sonic-mclag.yang new file mode 100644 index 0000000000..cfc5c47279 --- /dev/null +++ b/models/yang/sonic/sonic-mclag.yang @@ -0,0 +1,110 @@ +module sonic-mclag { + namespace "http://github.com/Azure/sonic-mclag"; + prefix smclag; + yang-version 1.1; + + import ietf-yang-types { + prefix yang; + } + + import ietf-inet-types { + prefix inet; + } + + import sonic-portchannel { + prefix spc; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC MCLAG"; + + revision 2019-10-01 { + description + "Initial revision."; + } + + container sonic-mclag { + + container MCLAG_DOMAIN { + + list MCLAG_DOMAIN_LIST { + key "domain_id"; + max-elements 1; + + leaf domain_id { + type uint16 { + range "1..4095" { + error-message "MCLAG Domain ID out of range"; + error-app-tag domain_id-invalid; + } + } + } + + leaf source_ip { + type inet:ipv4-address; + } + leaf peer_ip { + type inet:ipv4-address; + } + leaf peer_link { + type string; + } + leaf keepalive_interval { + type uint16 { + range "1..60" { + error-message "MCLAG Domain keepalive interval out of range"; + error-app-tag keepalive_interval-invalid; + } + } + default 1; + + } + leaf session_timeout { + type uint16 { + range "1..3600" { + error-message "MCLAG Domain session timeout out of range"; + error-app-tag session_timeout-invalid; + } + } + default 15; + } + must "(keeaplive_interval * 3) <= session_timeout" { + error-message "(keepalive interval * 3) <= session_timeout value"; + error-app-tag keepalive_interval-invalid; + } + must "session_timeout >= (3 *keepalive_interval)" { + error-message "(keepalive interval * 3) <= session_timeout value"; + error-app-tag session_timeout-invalid; + } + } + } + + container MCLAG_INTERFACE { + + list MCLAG_INTERFACE_LIST { + key "domain_id if_name"; + + leaf domain_id { + type leafref { + path "../../../MCLAG_DOMAIN/MCLAG_DOMAIN_LIST/domain_id"; + } + } + + leaf if_name { + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + leaf if_type { + type string; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-mirror-session.yang b/models/yang/sonic/sonic-mirror-session.yang new file mode 100644 index 0000000000..43d1dbd4a7 --- /dev/null +++ b/models/yang/sonic/sonic-mirror-session.yang @@ -0,0 +1,72 @@ +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; + } + + leaf dst_port { + type string; + } + + leaf src_port { + type string; + } + + leaf direction { + type string; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-port.yang b/models/yang/sonic/sonic-port.yang new file mode 100644 index 0000000000..44eb6eddd5 --- /dev/null +++ b/models/yang/sonic/sonic-port.yang @@ -0,0 +1,124 @@ +module sonic-port { + namespace "http://github.com/Azure/sonic-port"; + prefix prt; + + import sonic-common { + prefix scommon; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC PORT"; + + 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; + } + } + } + container PORT_TABLE { + config false; + + list PORT_TABLE_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 lanes { + type string; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf valid_speeds { + type string; + } + + leaf alias { + type string; + } + + leaf oper_status { + type scommon:oper-status; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-portchannel-interface.yang b/models/yang/sonic/sonic-portchannel-interface.yang new file mode 100644 index 0000000000..c9a9054267 --- /dev/null +++ b/models/yang/sonic/sonic-portchannel-interface.yang @@ -0,0 +1,58 @@ +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"; + + leaf pch_name{ + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + + } + list PORTCHANNEL_INTERFACE_IPADDR_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/models/yang/sonic/sonic-portchannel.yang b/models/yang/sonic/sonic-portchannel.yang new file mode 100644 index 0000000000..c70a80deea --- /dev/null +++ b/models/yang/sonic/sonic-portchannel.yang @@ -0,0 +1,136 @@ +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"; + + leaf name { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + 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"; + } + } + } + } + + container LAG_TABLE { + config false; + + list LAG_TABLE_LIST { + key "lagname"; + + leaf lagname { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf active { + type boolean; + } + + leaf name { + type string; + } + + leaf oper_status { + type scommon:oper-status; + } + } + } + + container LAG_MEMBER_TABLE { + config false; + list LAG_MEMBER_TABLE_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../LAG_TABLE/LAG_TABLE_LIST/lagname"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + } + } + } +} diff --git a/models/yang/sonic/sonic-ptp.yang b/models/yang/sonic/sonic-ptp.yang new file mode 100644 index 0000000000..eeeb6b3e1b --- /dev/null +++ b/models/yang/sonic/sonic-ptp.yang @@ -0,0 +1,254 @@ +module sonic-ptp { + namespace "http://github.com/Azure/sonic-ptp"; + prefix sptp; + yang-version 1.1; + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC PTP"; + + revision 2019-10-08 { + description + "Initial revision."; + } + + container sonic-ptp { + container PTP_INSTANCE { + list PTP_INSTANCE_LIST { + key instance-number; + leaf instance-number { + type string; + } + } + } + container PTP_CURRENTDS { + list PTP_CURRENTDS_LIST { + key instance-number; + leaf instance-number { + type leafref { + path "../../../PTP_INSTANCE/PTP_INSTANCE_LIST/instance-number"; + } + } + leaf steps-removed { + type uint16; + } + leaf offset-from-master { + type int64; + } + leaf mean-path-delay { + type int64; + } + } + } + container PTP_TC_CLOCK { + list PTP_TC_CLOCK_LIST { + key instance-number; + leaf instance-number { + type string { + pattern "GLOBAL" { + error-message "Invalid Instance"; + error-app-tag instance-number-invalid; + } + } + } + leaf clock-identity { + type string; + } + leaf number-ports { + type uint16; + } + leaf delay-mechanism { + type string; + } + leaf primary-domain { + type uint8; + } + } + } + container PTP_PARENTDS { + list PTP_PARENTDS_LIST { + key instance-number; + leaf instance-number { + type leafref { + path "../../../PTP_INSTANCE/PTP_INSTANCE_LIST/instance-number"; + } + } + leaf observed-parent-clock-phase-change-rate { + type int32; + } + leaf grandmaster-identity { + type string; + } + leaf grandmaster-priority1 { + type uint8; + } + leaf grandmaster-priority2 { + type uint8; + } + leaf port-number { + type uint16; + } + leaf observed-parent-offset-scaled-log-variance { + type uint16; + } + leaf clock-identity { + type string; + } + leaf offset-scaled-log-variance { + type uint16; + } + leaf clock-class { + type uint8; + } + leaf clock-accuracy { + type uint8; + } + leaf parent-stats { + type uint8; + } + } + } + container PTP_TIMEPROPDS { + list PTP_TIMEPROPDS_LIST { + key instance-number; + leaf instance-number { + type leafref { + path "../../../PTP_INSTANCE/PTP_INSTANCE_LIST/instance-number"; + } + } + leaf time-traceable { + type uint8; + } + leaf frequency-traceable { + type uint8; + } + leaf ptp-timescale { + type uint8; + } + leaf time-source { + type uint8; + } + leaf current-utc-offset-valid { + type uint8; + } + leaf current-utc-offset { + type int16; + } + leaf leap59 { + type uint8; + } + leaf leap61 { + type uint8; + } + } + } + container PTP_PORT { + list PTP_PORT_LIST { + key "instance-number port-number"; + leaf instance-number { + type leafref { + path "../../../PTP_INSTANCE/PTP_INSTANCE_LIST/instance-number"; + } + } + leaf port-number { + type string; + } + leaf peer-mean-path-delay { + type int64; + } + leaf announce-receipt-timeout { + type int8; + } + leaf log-sync-interval { + type int8; + } + leaf log-min-pdelay-req-interval { + type int8; + } + leaf version-number { + type int8; + } + leaf port-state { + type uint8; + } + leaf underlying-interface { + type string; + } + leaf log-min-delay-req-interval { + type int8; + } + leaf log-announce-interval { + type int8; + } + leaf delay-mechanism { + type string; + } + } + } + container PTP_TC_PORT { + list PTP_TC_PORT_LIST { + key "port-number"; + leaf port-number { + type string; + } + leaf log-min-pdelay-req-interval { + type int8; + } + leaf faulty-flag { + type uint8; + } + leaf peer-mean-path-delay { + type int64; + } + } + } + container PTP_CLOCK { + list PTP_CLOCK_LIST { + key instance-number; + leaf instance-number { + type leafref { + path "../../../PTP_INSTANCE/PTP_INSTANCE_LIST/instance-number"; + } + } + + // default-ds attributes + leaf priority1 { + type uint8; + } + leaf priority2 { + type uint8; + } + leaf domain-number { + type uint8; + } + leaf slave-only { + type uint8; + } + leaf clock-class { + type uint8; + } + leaf clock-accuracy { + type uint8; + } + leaf offset-scaled-log-variance { + type uint16; + } + leaf two-step-flag { + type uint8; + } + leaf clock-identity { + type string; + } + leaf number-ports { + type uint16; + } + + } + } + } +} diff --git a/models/yang/sonic/sonic-sflow.yang b/models/yang/sonic/sonic-sflow.yang new file mode 100644 index 0000000000..d8b6ebbf5c --- /dev/null +++ b/models/yang/sonic/sonic-sflow.yang @@ -0,0 +1,117 @@ +module sonic-sflow { + namespace "http://github.com/Azure/sonic-sflow"; + prefix sflow; + + import ietf-yang-types { + prefix yang; + } + + import ietf-inet-types { + prefix inet; + } + + import sonic-common { + prefix scommon; + } + + revision 2019-09-09 { + description + "Initial revision."; + } + + container sonic-sflow { + container SFLOW_COLLECTOR { + list SFLOW_COLLECTOR_LIST { + max-elements 2; + key "collector_name"; + + leaf collector_name { + type string { + length 1..16; + } + } + + leaf collector_ip { + type inet:ip-address; + } + + leaf collector_port { + type inet:port-number; + } + + } + } + + container SFLOW_SESSION { + list SFLOW_SESSION_LIST { + key "ifname"; + + leaf ifname { + type string; + } + + leaf admin_state { + type scommon:admin-status; + } + + leaf sample_rate { + type uint32 { + range "256..8388608" { + error-message "sFlow polling interval must be [256-8388608]"; + } + } + } + } + } + + container SFLOW { + list SFLOW_LIST { + key "sflow_key"; + leaf sflow_key { + type enumeration { + enum global; + } + } + + leaf admin_state { + type scommon:admin-status; + } + + leaf polling_interval { + type uint32 { + range "0|5..300" { + error-message "sFlow sample rate must be [0, 5-300]"; + } + } + } + + leaf agent_id { + type string; + } + } + } + + container SFLOW_SESSION_TABLE { + list SFLOW_SESSION_LIST { + key "ifname"; + config false; + + leaf ifname { + type string; + } + + leaf admin_state { + type scommon:admin-status; + } + + leaf sample_rate { + type uint32 { + range "256..8388608" { + error-message "sFlow polling interval must be [256-8388608]"; + } + } + } + } + } + } +} diff --git a/models/yang/sonic/sonic-udld.yang b/models/yang/sonic/sonic-udld.yang new file mode 100644 index 0000000000..88849f6f3b --- /dev/null +++ b/models/yang/sonic/sonic-udld.yang @@ -0,0 +1,184 @@ +module sonic-udld { + namespace "http://github.com/Azure/sonic-udld"; + prefix sudld; + yang-version 1.1; + + import sonic-extension { + prefix sonic-ext; + } + + import sonic-port { + prefix prt; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONiC UDLD yang model"; + + revision 2019-11-08 { + description + "Initial revision."; + } + + grouping udld_mode_config { + leaf admin_enable { + type boolean; + } + + leaf aggressive { + type boolean; + } + } + + container sonic-udld { + + container UDLD { + list UDLD_LIST { + key "id"; + + leaf id { + type enumeration { + enum GLOBAL; + } + } + + uses udld_mode_config; + + leaf msg_time { + type uint8 { + range "1..30" { + error-message "Invalid UDLD message time value."; + } + } + units seconds; + default 1; + } + + leaf multiplier { + type uint8 { + range "3..10" { + error-message "Invalid UDLD multiplier value."; + } + } + default 3; + } + } + } + + container UDLD_PORT { + list UDLD_PORT_LIST { + key "ifname"; + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + uses udld_mode_config; + } + } + + container UDLD_GLOBAL_TABLE { + config false; + + list UDLD_GLOBAL_TABLE_LIST { + sonic-ext:db-name "APPL_DB"; + key "id"; + + leaf id { + type enumeration { + enum GLOBAL; + } + } + + leaf device_id { + type string; + } + + leaf device_name { + type string; + } + + leaf timeout_interval { + type uint16; + } + } + } + + container UDLD_PORT_TABLE { + config false; + + list UDLD_PORT_TABLE_LIST { + sonic-ext:db-name "APPL_DB"; + key "ifname"; + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf status { + type string; + } + + leaf pdu_sent { + type uint64; + } + + leaf pdu_received { + type uint64; + } + + leaf pdu_recv_error { + type uint64; + } + } + } + + container UDLD_PORT_NEIGH_TABLE { + config false; + + list UDLD_PORT_NEIGH_TABLE_LIST { + sonic-ext:db-name "APPL_DB"; + key "ifname index"; + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf index { + type string; + } + + leaf device_id { + type string; + } + + leaf device_name { + type string; + } + + leaf port_id { + type string; + } + + leaf msg_time { + type uint16; + } + + leaf timeout_interval { + type uint16; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-vlan.yang b/models/yang/sonic/sonic-vlan.yang new file mode 100644 index 0000000000..0ea6bd6499 --- /dev/null +++ b/models/yang/sonic/sonic-vlan.yang @@ -0,0 +1,181 @@ +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"; + } + } + } + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf admin_status { + type scommon:admin-status; + } + } + } + + 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; + } + } + } + + + container VLAN_TABLE { + config false; + + list VLAN_TABLE_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 admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf oper_status { + type scommon:oper-status; + } + } + } + + container VLAN_MEMBER_TABLE { + config false; + + list VLAN_MEMBER_TABLE_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../VLAN_TABLE/VLAN_TABLE_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "/prt:sonic-port/prt:PORT/prt:PORT_LIST/prt:ifname"; + } + } + + leaf tagging_mode { + type scommon:tagging_mode; + } + } + } + } +} diff --git a/models/yang/sonic/sonic-vxlan.yang b/models/yang/sonic/sonic-vxlan.yang new file mode 100644 index 0000000000..c0795d4e78 --- /dev/null +++ b/models/yang/sonic/sonic-vxlan.yang @@ -0,0 +1,120 @@ +module sonic-vxlan { + namespace "http://github.com/Azure/sonic-vxlan"; + prefix svxlan; + yang-version 1.1; + + import ietf-yang-types { + prefix yang; + } + + import ietf-inet-types { + prefix inet; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VXLAN"; + + revision 2019-10-01 { + description + "Initial revision."; + } + + container sonic-vxlan { + + container VXLAN_TUNNEL { + + list VXLAN_TUNNEL_LIST { + key "name"; + max-elements 1; + + leaf name { + type string; + } + + leaf src_ip { + mandatory true; + type inet:ipv4-address; + } + } + } + + container VXLAN_TUNNEL_MAP { + + list VXLAN_TUNNEL_MAP_LIST { + key "name mapname"; + + leaf name { + type leafref { + path "../../../VXLAN_TUNNEL/VXLAN_TUNNEL_LIST/name"; + } + } + + leaf mapname { + type string; + } + + leaf vlan { + mandatory true; + 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 vni { + mandatory true; + type uint32 { + range "1..16777215" { + error-message "VNI ID out of range"; + error-app-tag vnid-invalid; + } + } + } + } + } + + container EVPN_NVO { + + list EVPN_NVO_LIST { + + key "name"; + max-elements 1; + + leaf name { + type string; + } + + leaf source_vtep { + mandatory true; + type leafref { + path "../../../VXLAN_TUNNEL/VXLAN_TUNNEL_LIST/name"; + } + } + } + } + container SUPPRESS_VLAN_NEIGH { + + list SUPPRESS_VLAN_NEIGH_LIST { + key "name"; + + leaf name { + type string; + } + leaf suppress { + type string; + } + + } + } + + } + +} diff --git a/models/yang/testdata/api-tests.yang b/models/yang/testdata/api-tests.yang new file mode 100644 index 0000000000..8fd519d09b --- /dev/null +++ b/models/yang/testdata/api-tests.yang @@ -0,0 +1,34 @@ +module api-tests { + + yang-version "1"; + namespace "http://github.com/Azure/sonic-api-tests"; + prefix "api-tests"; + + description + "Test module for SONiC Management REST Server"; + + + container home { + leaf name { + type string; + } + } + + rpc my-echo { + input { + leaf message { + type string; + } + leaf error-type { + type string; + } + } + + output { + leaf message { + type string; + } + } + } + +} \ No newline at end of file diff --git a/models/yang/testdata/sonic-tests-annot.yang b/models/yang/testdata/sonic-tests-annot.yang new file mode 100644 index 0000000000..5184080ecd --- /dev/null +++ b/models/yang/testdata/sonic-tests-annot.yang @@ -0,0 +1,18 @@ +module sonic-tests-annot { + + yang-version "1"; + + namespace "http://openconfig.net/yang/annotation"; + prefix "sflow-annot"; + + import sonic-extensions { prefix sonic-ext; } + import sonic-tests { prefix tests; } + + deviation /tests:sum { + deviate add { + sonic-ext:rpc-callback "rpc_sum_cb"; + } + } + +} + diff --git a/models/yang/testdata/sonic-tests.yang b/models/yang/testdata/sonic-tests.yang new file mode 100644 index 0000000000..bb5cd4cd35 --- /dev/null +++ b/models/yang/testdata/sonic-tests.yang @@ -0,0 +1,95 @@ +module sonic-tests { + namespace "http://github.com/Azure/sonic-tests"; + prefix test; + + import sonic-common { + prefix scommon; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC TEST Module for Transformer/common-app use"; + + revision 2019-10-16 { + description + "Initial revision."; + } + + + container sonic-tests { + + container TABLE_A { + + list TABLE_A_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; + } + } + } + } + + rpc sum { + input { + leaf left { + type int32; + } + leaf right { + type int32; + } + } + output { + leaf result { + type int32; + } + } + } +} diff --git a/patches/jsonquery.patch b/patches/jsonquery.patch new file mode 100644 index 0000000000..8fcc49910b --- /dev/null +++ b/patches/jsonquery.patch @@ -0,0 +1,32 @@ +diff --git a/node.go b/node.go +index 76032bb..9960c17 100644 +--- a/node.go ++++ b/node.go +@@ -110,6 +110,17 @@ func parseValue(x interface{}, top *Node, level int) { + addNode(n) + parseValue(vv, n, level+1) + } ++ case map[string]string: ++ var keys []string ++ for key := range v { ++ keys = append(keys, key) ++ } ++ sort.Strings(keys) ++ for _, key := range keys { ++ n := &Node{Data: key, Type: ElementNode, level: level} ++ addNode(n) ++ parseValue(v[key], n, level+1) ++ } + case map[string]interface{}: + // The Go’s map iteration order is random. + // (https://blog.golang.org/go-maps-in-action#Iteration-order) +@@ -155,3 +166,9 @@ func Parse(r io.Reader) (*Node, error) { + } + return parse(b) + } ++ ++func ParseJsonMap(jsonMap *map[string]interface{}) (*Node, error) { ++ doc := &Node{Type: DocumentNode} ++ parseValue(*jsonMap, doc, 1) ++ return doc, nil ++} diff --git a/patches/xmlquery.patch b/patches/xmlquery.patch new file mode 100644 index 0000000000..acae08fee1 --- /dev/null +++ b/patches/xmlquery.patch @@ -0,0 +1,33 @@ +diff --git a/node.go b/node.go +index e86c0c3..028867c 100644 +--- a/node.go ++++ b/node.go +@@ -48,7 +48,7 @@ type Node struct { + + // InnerText returns the text between the start and end tags of the object. + func (n *Node) InnerText() string { +- var output func(*bytes.Buffer, *Node) ++ /*var output func(*bytes.Buffer, *Node) + output = func(buf *bytes.Buffer, n *Node) { + switch n.Type { + case TextNode: +@@ -64,7 +64,18 @@ func (n *Node) InnerText() string { + + var buf bytes.Buffer + output(&buf, n) +- return buf.String() ++ return buf.String()*/ ++ ++ if (n.Type == TextNode) { ++ return n.Data ++ } else if (n.Type == ElementNode) && ++ (n.FirstChild != nil) && ++ (n.FirstChild.Type == TextNode) { ++ return n.FirstChild.Data ++ } ++ ++ ++ return "" + } + + func (n *Node) sanitizedData(preserveSpaces bool) string { diff --git a/scripts/host_modules/host_service.py b/scripts/host_modules/host_service.py new file mode 100644 index 0000000000..48c65161d1 --- /dev/null +++ b/scripts/host_modules/host_service.py @@ -0,0 +1,20 @@ +"""Base class for host modules""" + +import dbus.service +import dbus + +BUS_NAME_BASE = 'org.SONiC.HostService' +BUS_PATH = '/org/SONiC/HostService' + +def bus_name(mod_name): + """Return the bus name for the service""" + return BUS_NAME_BASE + '.' + mod_name + +method = dbus.service.method + +class HostModule(dbus.service.Object): + """Base class for all host modules""" + def __init__(self, mod_name): + self.bus = dbus.SystemBus() + self.bus_name = dbus.service.BusName(BUS_NAME_BASE, self.bus) + super(HostModule, self).__init__(self.bus_name, BUS_PATH) diff --git a/scripts/host_modules/ztp.py b/scripts/host_modules/ztp.py new file mode 100644 index 0000000000..5bcf84dd50 --- /dev/null +++ b/scripts/host_modules/ztp.py @@ -0,0 +1,43 @@ +"""ZTP command handler""" + +import host_service +import subprocess + +MOD_NAME = 'ztp' + +class ZTP(host_service.HostModule): + """DBus endpoint that executes ZTP related commands + """ + @staticmethod + def _run_command(commands): + """Run a ZTP command""" + cmd = ['/usr/bin/ztp'] + if isinstance(commands, list): + cmd.extend(commands) + else: + cmd.append(commands) + + try: + rc = 0 + output = subprocess.check_output(cmd) + except subprocess.CalledProcessError as err: + rc = err.returncode + output = "" + + return rc, output + + @host_service.method(host_service.bus_name(MOD_NAME), in_signature='', out_signature='') + def enable(self): + self._run_command("enable") + + @host_service.method(host_service.bus_name(MOD_NAME), in_signature='', out_signature='') + def disable(self): + self._run_command(["disable", "-y"]) + + @host_service.method(host_service.bus_name(MOD_NAME), in_signature='', out_signature='is') + def status(self): + return self._run_command("status") + +def register(): + """Return the class name""" + return ZTP diff --git a/scripts/org.sonic.hostservice.conf b/scripts/org.sonic.hostservice.conf new file mode 100644 index 0000000000..08599007d9 --- /dev/null +++ b/scripts/org.sonic.hostservice.conf @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + diff --git a/scripts/sonic-hostservice.service b/scripts/sonic-hostservice.service new file mode 100644 index 0000000000..923e2cbd2d --- /dev/null +++ b/scripts/sonic-hostservice.service @@ -0,0 +1,15 @@ +[Unit] +Description=SONiC Host Service + +[Service] +Type=dbus +BusName=org.SONiC.HostService + +ExecStart=/usr/bin/python2 -u /usr/lib/sonic_host_service/sonic_host_server.py + +Restart=on-failure +RestartSec=3 +TimeoutStopSec=3 + +[Install] +WantedBy=multi-user.target diff --git a/scripts/sonic_host_server.py b/scripts/sonic_host_server.py new file mode 100755 index 0000000000..88ca741f7b --- /dev/null +++ b/scripts/sonic_host_server.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python2 +"""Host Service to handle docker-to-host communication""" + +import os +import os.path +import glob +import importlib +import sys + +import dbus +import dbus.service +import dbus.mainloop.glib +import gobject + +BUS_NAME = 'org.SONiC.HostService' +BUS_PATH = '/org/SONiC/HostService' + +def register_modules(): + """Register all host modules""" + mod_path = os.path.join(os.path.dirname(__file__), 'host_modules') + sys.path.append(mod_path) + for mod_file in glob.glob(os.path.join(mod_path, '*.py')): + if os.path.isfile(mod_file) and not mod_file.endswith('__init__.py') \ + and not mod_file.endswith('host_service.py'): + mod_name = os.path.basename(mod_file)[:-3] + module = importlib.import_module(mod_name) + + register_cb = getattr(module, 'register', None) + if not register_cb: + raise Exception('Missing register function for ' + mod_name) + + register_dbus(mod_name, register_cb()) + +def register_dbus(mod_name, handler_class): + """Register DBus handlers for individual modules""" + handlers[mod_name] = handler_class(mod_name) + +# Create a main loop reactor +gobject.threads_init() +dbus.mainloop.glib.threads_init() +dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) +loop = gobject.MainLoop() +handlers = {} + +class SignalManager(object): + ''' This is used to manage signals received (e.g. SIGINT). + When stopping a process (systemctl stop [service]), systemd sends + a SIGTERM signal. + ''' + shutdown = False + def __init__(self): + ''' Install signal handlers. + + SIGTERM is invoked when systemd wants to stop the daemon. + For example, "systemctl stop mydaemon.service" + or, "systemctl restart mydaemon.service" + + ''' + import signal + signal.signal(signal.SIGTERM, self.sigterm_hdlr) + + def sigterm_hdlr(self, _signum, _frame): + self.shutdown = True + loop.quit() + +sigmgr = SignalManager() +register_modules() + +# Only run if we actually have some handlers +if handlers: + import systemd.daemon + systemd.daemon.notify("READY=1") + + while not sigmgr.shutdown: + loop.run() + if sigmgr.shutdown: + break + + systemd.daemon.notify("STOPPING=1") +else: + print "No handlers to register, quitting..." diff --git a/src/.gitkeep b/src/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/CLI/.gitkeep b/src/CLI/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/CLI/Makefile b/src/CLI/Makefile new file mode 100644 index 0000000000..26ead3991b --- /dev/null +++ b/src/CLI/Makefile @@ -0,0 +1,55 @@ +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 + +TOPDIR ?= $(abspath ../..) +CLIDEPS ?= packages +SUBDIRS := clitree renderer klish +export SONIC_CLI_ROOT=$(TOPDIR)/build +TGT_DIR := $(SONIC_CLI_ROOT)/target + +all: $(CLIDEPS) $(SUBDIRS) +$(SUBDIRS): + $(MAKE) -C $@ + +.PHONY: clean + +all: + for dir in $(SUBDIRS); do \ + $(MAKE) -C $$dir -f Makefile $@; \ + done + + rm -rf $(TOPDIR)/build/cli + mv -f $(TGT_DIR) $(TOPDIR)/build/cli + +clean: + make --directory=klish clean + rm -rf $(TOPDIR)/build/cli + rm -rf $(TGT_DIR) + +packages: + if ! dpkg -l | grep autoconf -c >>/dev/null; then sudo apt-get install autoconf; fi + if ! dpkg -l | grep m4 -c >>/dev/null; then sudo apt-get install m4; fi + if ! dpkg -l | grep libxml2-utils -c >>/dev/null; then sudo apt-get install libxml2-utils; fi + if ! dpkg -l | grep xsltproc -c >>/dev/null; then sudo apt-get install xsltproc; fi + if ! dpkg -l | grep python-lxml -c >>/dev/null; then sudo apt-get install python-lxml; fi + if ! dpkg -l | grep libexpat1-dev -c >>/dev/null; then sudo apt-get install libexpat1-dev; fi + diff --git a/src/CLI/actioner/README_cli_client.md b/src/CLI/actioner/README_cli_client.md new file mode 100644 index 0000000000..02ea91c11c --- /dev/null +++ b/src/CLI/actioner/README_cli_client.md @@ -0,0 +1,88 @@ +# Generic REST Client for CLI + +Actioner scripts can use the generic rest client **cli_client.py** to communicate to REST Server. +This client is tailor made to connect to local REST Server and handle the CLI actioner usecases. +All future CLI-REST integration enhancements (for RBAC) can be handled in this tool without +affecting individual actioner scripts. + +To use this tool, first create an `ApiClient` object. + +```python +import cli_client as cc + +api = cc.ApiClient() +``` + +Create a path object for target REST resource. It accepts parameterized path template and parameter +values. Path template is similar to the template used by swagger. Parameter values will be URL-encoded +and substituted in the template to get REST resource path. + +```python +path = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{type}/acl-entries', + name='MyNewAccessList', type='ACL_IPV4') +``` + +Invoke REST API.. `ApiClient` object supports get, post, put, patch and delete operations. +All these operations send a REST request and return a response object wrapping the REST response data. + +```python +response = api.get(path) +``` + +Check API status through `response.ok()` function, which returns true if API was success - REST server +returned HTTP 2xx status code. `ok()` function returns false if server returned any other status response. + +```python +if response.ok() { + respJson = response.content + # invoke renderer to display respJson +} else { + print(response.error_message()) +} +``` + +If request was successful, `response.content` will hold the response data as JSON dictionary object. +If request failed, `response.content` will hold error JSON returned by the server. CLI displayable +error message can be extracted using `response.error_message()` function. + +Examples of other REST API calls. + +```python +jsonDict = {} +jsonDict["acl-set"]=[{ "name":the_acl_name, .... }] # construct your request data json + +# POST request +response = api.post(path, data=jsonDict) + +# PUT request +reponse = api.put(path, data=jsonDict) + +# PATCH request +response = api.patch(path, data=jsonDict) + +# DELETE request +response = api.delete(path) +``` + +Above example used `response.error_message()` function to get ser displayable error message from +the REST response. REST server always returns error information in standard RESTCONF error format. +The `error_message()` function looks for the **error-message** attribute to get user displayable message. +A generic message will be returned based on **error-tag** attribute value if there was no **error-message** +attribute. This can be customized through an error formatter function as shown below. +Default error formatter is sufficient for most of the cases. + +```python +if not response.ok(): + print(response.error_message(formatter_func=my_new_error_message_formatter)) +``` + +The custom formtter function would receive RESTCONF error json and should return a string. +Below is a sample implementation which uses error-type attribute to format an error message. + +```python +def my_new_error_message_formatter(error_entry): + if err_entry['error-type'] == 'protocol': + return "System error.. Please reboot!!" + return "Application error.. Please retry." +``` + diff --git a/src/CLI/actioner/cli_client.py b/src/CLI/actioner/cli_client.py new file mode 100644 index 0000000000..5cfda60061 --- /dev/null +++ b/src/CLI/actioner/cli_client.py @@ -0,0 +1,173 @@ +################################################################################ +# # +# 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 os +import json +import urllib3 +from six.moves.urllib.parse import quote + +urllib3.disable_warnings() + +class ApiClient(object): + """ + A client for accessing a RESTful API + """ + + def __init__(self): + """ + Create a RESTful API client. + """ + self.api_uri = os.getenv('REST_API_ROOT', 'https://localhost') + + self.checkCertificate = False + + self.version = "0.0.1" + + def set_headers(self): + from requests.structures import CaseInsensitiveDict + return CaseInsensitiveDict({ + 'User-Agent': "CLI" + }) + + @staticmethod + def merge_dicts(*dict_args): + result = {} + for dictionary in dict_args: + result.update(dictionary) + return result + + def request(self, method, path, data=None, headers={}): + from requests import request, RequestException + + url = '{0}{1}'.format(self.api_uri, path) + + req_headers = self.set_headers() + req_headers.update(headers) + + body = None + if data is not None: + if 'Content-Type' not in req_headers: + req_headers['Content-Type'] = 'application/yang-data+json' + body = json.dumps(data) + + try: + r = request(method, url, headers=req_headers, data=body, verify=self.checkCertificate) + return Response(r) + except RequestException: + #TODO have more specific error message based + return self._make_error_response('%Error: Could not connect to Management REST Server') + + def post(self, path, data={}): + return self.request("POST", path, data) + + def get(self, path): + return self.request("GET", path, None) + + def put(self, path, data={}): + return self.request("PUT", path, data) + + def patch(self, path, data={}): + return self.request("PATCH", path, data) + + def delete(self, path): + return self.request("DELETE", path, None) + + @staticmethod + def _make_error_response(errMessage, errType='client', errTag='operation-failed'): + import requests + r = Response(requests.Response()) + r.content = {'ietf-restconf:errors':{ 'error':[ { + 'error-type':errType, 'error-tag':errTag, 'error-message':errMessage }]}} + return r + + def cli_not_implemented(self, hint): + return self._make_error_response('%Error: not implemented {0}'.format(hint)) + + +class Path(object): + def __init__(self, template, **kwargs): + self.template = template + self.params = kwargs + self.path = template + for k, v in kwargs.items(): + self.path = self.path.replace('{%s}' % k, quote(v, safe='')) + + def __str__(self): + return self.path + + +class Response(object): + def __init__(self, response): + self.response = response + + try: + if response.content is None or len(response.content) == 0: + self.content = None + else: + self.content = self.response.json() + except ValueError: + self.content = self.response.text + + def ok(self): + return self.response.status_code >= 200 and self.response.status_code <= 299 + + def errors(self): + if self.ok(): + return {} + + errors = self.content + + if(not isinstance(errors, dict)): + errors = {"error": errors} # convert to dict for consistency + elif('ietf-restconf:errors' in errors): + errors = errors['ietf-restconf:errors'] + + return errors + + def error_message(self, formatter_func=None): + err = self.errors().get('error') + if err == None: + return None + if isinstance(err, list): + err = err[0] + if isinstance(err, dict): + if formatter_func is not None: + return formatter_func(err) + return default_error_message_formatter(err) + return str(err) + + def __getitem__(self, key): + return self.content[key] + + +def default_error_message_formatter(err_entry): + if 'error-message' in err_entry: + err_msg = err_entry['error-message'] + if not err_msg.startswith("%Error"): + return '%Error: ' + err_msg + return err_msg + err_tag = err_entry.get('error-tag') + if err_tag == 'invalid-value': + return '%Error: validation failed' + if err_tag == 'operation-not-supported': + return '%Error: not supported' + if err_tag == 'access-denied': + return '%Error: not authorized' + return '%Error: operation failed' + diff --git a/src/CLI/actioner/drop-monitor.py b/src/CLI/actioner/drop-monitor.py new file mode 100755 index 0000000000..2269c3c510 --- /dev/null +++ b/src/CLI/actioner/drop-monitor.py @@ -0,0 +1,290 @@ +#!/usr/bin/python + +############################################################################ +# +# drop-monitor is a tool for handling DROP MONITOR Feature commands. +# +############################################################################ + +import argparse +import getopt +import json +import os +import re +import sys +import swsssdk +from swsssdk import ConfigDBConnector +from os import path +from scripts.render_cli import show_cli_output + +TAM_COLLECTOR_TABLE_PREFIX = "TAM_COLLECTOR_TABLE" +SAMPLE_RATE_TABLE = "SAMPLE_RATE_TABLE" +TAM_DROP_MONITOR_AGING_INTERVAL_TABLE = "TAM_DROP_MONITOR_AGING_INTERVAL_TABLE" +TAM_DROP_MONITOR_FLOW_TABLE = "TAM_DROP_MONITOR_FLOW_TABLE" +ACL_RULE_TABLE_PREFIX = "ACL_RULE" +ACL_TABLE_PREFIX = "ACL_TABLE" + +collectorheader = ['NAME', 'IP TYPE', 'IP', 'PORT'] + +class DropMon(object): + + def __init__(self): + # connect CONFIG DB + self.config_db = ConfigDBConnector() + self.config_db.connect() + + # connect COUNTERS_DB + self.counters_db = ConfigDBConnector() + self.counters_db.db_connect('COUNTERS_DB') + + # connect APPL DB + self.app_db = ConfigDBConnector() + self.app_db.db_connect('APPL_DB') + + + def config_drop_mon(self, args): + self.config_db.mod_entry(TAM_DROP_MONITOR_FLOW_TABLE, args.flowname, {'acl-table' : args.acl_table, 'acl-rule' : args.acl_rule, 'collector' : args.dropcollector, 'sample' : args.dropsample}) + return + + def config_drop_mon_aging(self, args): + self.config_db.mod_entry(TAM_DROP_MONITOR_AGING_INTERVAL_TABLE, "aging", {'aging-interval' : args.aginginterval}) + return + + def config_drop_mon_sample(self, args): + self.config_db.mod_entry(SAMPLE_RATE_TABLE, args.samplename, {'sampling-rate' : args.rate}) + return + + def clear_single_drop_mon_flow(self, key): + entry = self.config_db.get_entry(TAM_DROP_MONITOR_FLOW_TABLE, key) + if entry: + self.config_db.set_entry(TAM_DROP_MONITOR_FLOW_TABLE, key, None) + else: + return False + return + + def clear_drop_mon_flow(self, args): + key = args.flowname + if key == "all": + # Get all the flow keys + table_data = self.config_db.get_keys(TAM_DROP_MONITOR_FLOW_TABLE) + if not table_data: + return True + # Clear each flow key + for key in table_data: + self.clear_single_drop_mon_flow(key) + else: + # Clear the specified flow entry + self.clear_single_drop_mon_flow(key) + + return + + def clear_drop_mon_sample(self, args): + key = args.samplename + entry = self.config_db.get_entry(SAMPLE_RATE_TABLE, key) + if entry: + self.config_db.set_entry(SAMPLE_RATE_TABLE, key, None) + else: + print "Entry Not Found" + return False + return + + def clear_drop_mon_aging_int(self, args): + key = "aging" + entry = self.config_db.get_entry(TAM_DROP_MONITOR_AGING_INTERVAL_TABLE, key) + if entry: + self.config_db.set_entry(TAM_DROP_MONITOR_AGING_INTERVAL_TABLE, key, None) + else: + return False + return + + def show_flow(self, args): + self.get_print_all_dropmon_flows(args.flowname) + return + + def get_dropmon_flow_stat(self, flowname): + api_response_stat = {} + api_response, entryfound = self.get_dropmon_flow_info(flowname) + api_response_stat['flow-name'] = flowname + if entryfound is not None: + for k in api_response: + if k == "ietf-ts:each-flow-data": + acl_rule = api_response['ietf-ts:each-flow-data']['acl-rule'] + acl_table = api_response['ietf-ts:each-flow-data']['acl-table'] + api_response_stat['rule-name'] = acl_rule + api_response_stat['table-name'] = acl_table + + acl_rule_keys = self.config_db.get_keys(ACL_RULE_TABLE_PREFIX) + for acl_rule_key in acl_rule_keys: + if acl_rule_key[1] == acl_rule: + acl_counter_key = 'COUNTERS:' + acl_rule_key[0] + ':' + acl_rule_key[1] + raw_dropmon_stats = self.counters_db.get_all(self.counters_db.COUNTERS_DB, acl_counter_key) + api_response_stat['ietf-ts:dropmon-stats'] = raw_ifa_stats + + return api_response_stat, entryfound + + def get_print_all_dropmon_stats(self, name): + stat_dict = {} + stat_list = [] + if name != 'all': + api_response, entryfound = self.get_dropmon_flow_stat(name) + if entryfound is not None: + stat_list.append(api_response) + else: + table_data = self.config_db.get_keys(TAM_DROP_MONITOR_FLOW_TABLE) + # Get data for all keys + for k in table_data: + api_each_stat_response, entryfound = self.get_dropmon_flow_stat(k) + if entryfound is not None: + stat_list.append(api_each_stat_response) + + stat_dict['stat-list'] = stat_list + show_cli_output("show_statistics_flow.j2", stat_dict) + return + + def show_statistics(self, args): + self.get_print_all_dropmon_stats(args.flowname) + return + + def show_aging_interval(self, args): + key = "aging" + entry = self.config_db.get_entry(TAM_DROP_MONITOR_AGING_INTERVAL_TABLE, key) + if entry: + print "Aging interval : {}".format(entry['aging-interval']) + return + + def show_sample(self, args): + self.get_print_all_sample(args.samplename) + return + + def get_dropmon_flow_info(self, k): + flow_data = {} + flow_data['acl-table-name'] = '' + flow_data['sampling-rate'] = '' + flow_data['collector'] = '' + + api_response = {} + key = TAM_DROP_MONITOR_FLOW_TABLE + '|' + k + raw_flow_data = self.config_db.get_all(self.config_db.CONFIG_DB, key) + if raw_flow_data: + sample = raw_flow_data['sample'] + rate = self.config_db.get_entry(SAMPLE_RATE_TABLE, sample) + raw_flow_data['sample'] = rate['sampling-rate'] + api_response['ietf-ts:flow-key'] = k + api_response['ietf-ts:each-flow-data'] = raw_flow_data + return api_response , raw_flow_data + + def get_print_all_dropmon_flows(self, name): + flow_dict = {} + flow_list = [] + if name != 'all': + api_response, entryfound = self.get_dropmon_flow_info(name) + if entryfound is not None: + flow_list.append(api_response) + else: + table_data = self.config_db.get_keys(TAM_DROP_MONITOR_FLOW_TABLE) + # Get data for all keys + for k in table_data: + api_each_flow_response, entryfound = self.get_dropmon_flow_info(k) + if entryfound is not None: + flow_list.append(api_each_flow_response) + + flow_dict['flow-list'] = flow_list + show_cli_output("show_drop_monitor_flow.j2", flow_dict) + return + + def get_sample_info(self, k): + sample_data = {} + sample_data['sampling-rate'] = '' + + api_response = {} + key = SAMPLE_RATE_TABLE + '|' + k + raw_sample_data = self.config_db.get_all(self.config_db.CONFIG_DB, key) + api_response['ietf-ts:sample-key'] = k + api_response['ietf-ts:each-sample-data'] = raw_sample_data + return api_response , raw_sample_data + + def get_print_all_sample(self, name): + sample_dict = {} + sample_list = [] + if name != 'all': + api_response, entryfound = self.get_sample_info(name) + if entryfound is not None: + sample_list.append(api_response) + else: + table_data = self.config_db.get_keys(SAMPLE_RATE_TABLE) + # Get data for all keys + for k in table_data: + api_each_flow_response, entryfound = self.get_sample_info(k) + if entryfound is not None: + sample_list.append(api_each_flow_response) + + sample_dict['sample-list'] = sample_list + show_cli_output("show_sample.j2", sample_dict) + return + +def main(): + + parser = argparse.ArgumentParser(description='Handles MoD commands', + version='1.0.0', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""" +Examples: + drop-monitor -config -dropmonitor -flow flowname --acl_table acltablename --acl_rule aclrulename --dropcollector collectorname --dropsample samplename + drop-monitor -config -dropmonitor --aginginterval interval + drop-monitor -config -sample samplename --rate samplingrate +""") + + parser.add_argument('-clear', '--clear', action='store_true', help='Clear mod information') + parser.add_argument('-show', '--show', action='store_true', help='Show mod information') + parser.add_argument('-config', '--config', action='store_true', help='Config mod information') + #Drop Monitor params + parser.add_argument('-dropmonitor', '--dropmonitor', action='store_true', help='Configure Drop Monitor') + parser.add_argument('-flow', '--flowname', type=str, help='Flowname') + parser.add_argument('-acl_table', '--acl_table', type=str, help='ACL Table Name') + parser.add_argument('-acl_rule', '--acl_rule', type=str, help='ACL Rule Name') + parser.add_argument('-dropcollector', '--dropcollector', type=str, help='Drop Monitor Collector Name') + parser.add_argument('-dropsample', '--dropsample', type=str, help='Drop Monitor Sample Name') + parser.add_argument('-aginginterval', '--aginginterval', type=int, help='Drop Monitor Aging Interval') + parser.add_argument('-rate', '--rate', type=int, help='Sampling rate') + #Sample Params + parser.add_argument('-sample', '--samplename', type=str, help='Sample Name') + parser.add_argument('-statistics', '--statistics', action='store_true', help='drop monitor statistics') + parser.add_argument('-templ', '--template', action='store_true', help='drop monitor template to be used') + parser.add_argument('-showflow.j2', '--showflow', action='store_true', help='dropmon flow to be used') + parser.add_argument('-showstatisticsflow.j2', '--showstatistics', action='store_true', help='Flow statistics') + parser.add_argument('-showsample.j2', '--showsample', action='store_true', help='Sample configuration') + + args = parser.parse_args() + + dropmon = DropMon() + + if args.config: + if args.dropmonitor: + if args.aginginterval: + dropmon.config_drop_mon_aging(args) + elif args.flowname and args.acl_table and args.acl_rule and args.dropcollector and args.dropsample: + dropmon.config_drop_mon(args) + elif args.samplename and args.rate: + dropmon.config_drop_mon_sample(args) + elif args.clear: + if args.dropmonitor: + if args.flowname: + dropmon.clear_drop_mon_flow(args) + else: + dropmon.clear_drop_mon_aging_int(args) + elif args.samplename: + dropmon.clear_drop_mon_sample(args) + elif args.show: + if args.statistics: + dropmon.show_statistics(args) + elif args.flowname: + dropmon.show_flow(args) + elif args.aginginterval: + dropmon.show_aging_interval(args) + elif args.samplename: + dropmon.show_sample(args) + + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/src/CLI/actioner/sonic-cli-acl.py b/src/CLI/actioner/sonic-cli-acl.py new file mode 100755 index 0000000000..231e127d2e --- /dev/null +++ b/src/CLI/actioner/sonic-cli-acl.py @@ -0,0 +1,260 @@ +#!/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 json +import collections +import re +import cli_client as cc +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output + + +def invoke(func, args): + body = None + aa = cc.ApiClient() + + # Get the rules of all ACL table entries. + if func == 'get_openconfig_acl_acl_acl_sets': + keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets') + return aa.get(keypath) + + # Get Interface binding to ACL table info + if func == 'get_openconfig_acl_acl_interfaces': + keypath = cc.Path('/restconf/data/openconfig-acl:acl/interfaces') + return aa.get(keypath) + + # Get all the rules specific to an ACL table. + if func == 'get_openconfig_acl_acl_acl_sets_acl_set_acl_entries': + keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{type}/acl-entries', + name=args[0], type=args[1] ) + return aa.get(keypath) + + # Configure ACL table + if func == 'patch_openconfig_acl_acl_acl_sets_acl_set' : + keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{type}', + name=args[0], type=args[1] ) + body=collections.defaultdict(dict) + body["acl-set"]=[{ + "name": args[0], + "type": args[1], + "config": { + "name": args[0], + "type": args[1], + "description": "" + } + }] + + return aa.patch(keypath, body) + + # Configure ACL rule specific to an ACL table + if func == 'patch_list_openconfig_acl_acl_acl_sets_acl_set_acl_entries_acl_entry' : + keypath = cc.Path('/restconf/data/openconfig-acl:acl/acl-sets/acl-set={name},{type}/acl-entries/acl-entry', + name=args[0], type=args[1] ) + forwarding_action = "ACCEPT" if args[3] == 'permit' else 'DROP' + proto_number = {"icmp":"IP_ICMP","tcp":"IP_TCP","udp":"IP_UDP","6":"IP_TCP","17":"IP_UDP","1":"IP_ICMP", + "2":"IP_IGMP","103":"IP_PIM","46":"IP_RSVP","47":"IP_GRE","51":"IP_AUTH","115":"IP_L2TP"} + if args[4] not in proto_number.keys(): + print("%Error: Invalid protocol number") + exit(1) + else: + protocol = proto_number.get(args[4]) + body=collections.defaultdict(dict) + body["acl-entry"]=[{ + "sequence-id": int(args[2]), + "config": { + "sequence-id": int(args[2]) + }, + "ipv4":{ + "config":{ + "protocol": protocol + } + }, + "transport": { + "config": { + } + }, + "actions": { + "config": { + "forwarding-action": forwarding_action + } + } + }] + re_ip = re.compile("^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") + if re_ip.match(args[5]): + body["acl-entry"][0]["ipv4"]["config"]["source-address"]=args[5] + elif args[5]=="any": + body["acl-entry"][0]["ipv4"]["config"]["source-address"]="0.0.0.0/0" + flags_list=[] + i=6 + while(i%s : %s\n" %(func.__name__, e)) + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Transaction Failure" + return + print "%Error: Transaction Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + func = eval(sys.argv[1], globals(), openconfig_interfaces_client.OpenconfigInterfacesApi.__dict__) + + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-img.py b/src/CLI/actioner/sonic-cli-img.py new file mode 100644 index 0000000000..f504a6a564 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-img.py @@ -0,0 +1,93 @@ +#!/usr/bin/python +import sys +import time +import json +import ast +import openconfig_platform_client +from rpipe_utils import pipestr +from openconfig_platform_client.rest import ApiException +from scripts.render_cli import show_cli_output + + +import urllib3 +urllib3.disable_warnings() + +plugins = dict() +temp_resp = {'Current': 'SONiC-OS-HEAD.XXXX','Next': 'SONiC-OS-HEAD.XXXX','Available':['SONiC-OS-HEAD.YYYY', 'SONiC-OS-HEAD.XXXX']} + +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 generate_body(func, args): + body = None + # Get the rules of all ACL table entries. + + if func.__name__ == 'get_openconfig_platform_components': + keypath = [] + + else: + body = {} + + return keypath,body + + +def run(func, args): + c = openconfig_platform_client.Configuration() + c.verify_ssl = False + aa = openconfig_platform_client.OpenconfigPlatformApi(api_client=openconfig_platform_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + + else : + api_response = getattr(aa,func.__name__)(*keypath) + + if api_response is None: + print ("Success") + else: + #api_response = aa.api_client.sanitize_for_serialization(api_response) + if 'image-list' in sys.argv: + show_cli_output(sys.argv[2],temp_resp) + else: + print('Success') + + except ApiException as e: + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Transaction Failure" + return + print "%Error: Transaction Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + #pdb.set_trace() + func = eval(sys.argv[1], globals(), openconfig_platform_client.OpenconfigPlatformApi.__dict__) + run(func, sys.argv[2:]) + diff --git a/src/CLI/actioner/sonic-cli-lldp.py b/src/CLI/actioner/sonic-cli-lldp.py new file mode 100644 index 0000000000..11d71479b2 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-lldp.py @@ -0,0 +1,99 @@ +#!/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 +import ast +import openconfig_lldp_client +from rpipe_utils import pipestr +from openconfig_lldp_client.rest import ApiException +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 generate_body(func, args): + body = None + if func.__name__ == 'get_openconfig_lldp_lldp_interfaces': + keypath = [] + elif func.__name__ == 'get_openconfig_lldp_lldp_interfaces_interface': + keypath = [args[1]] + else: + body = {} + + return keypath,body + + +def run(func, args): + c = openconfig_lldp_client.Configuration() + c.verify_ssl = False + aa = openconfig_lldp_client.OpenconfigLldpApi(api_client=openconfig_lldp_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + else : + api_response = getattr(aa,func.__name__)(*keypath) + if api_response is None: + print ("Success") + else: + response = api_response.to_dict() + if 'openconfig_lldpinterfaces' in response.keys(): + if not response['openconfig_lldpinterfaces']: + return + neigh_list = response['openconfig_lldpinterfaces']['interface'] + if neigh_list is None: + return + show_cli_output(sys.argv[2],neigh_list) + elif 'openconfig_lldpinterface' in response.keys(): + neigh = response['openconfig_lldpinterface']#[0]['neighbors']['neighbor'] + if neigh is None: + return + if sys.argv[3] is not None: + if neigh[0]['neighbors']['neighbor'][0]['state'] is None: + print('No LLDP neighbor interface') + else: + show_cli_output(sys.argv[2],neigh) + else: + show_cli_output(sys.argv[2],neigh) + else: + print("Failed") + except ApiException as e: + print("Exception when calling OpenconfigLldpApi->%s : %s\n" %(func.__name__, e)) + +if __name__ == '__main__': + pipestr().write(sys.argv) + func = eval(sys.argv[1], globals(), openconfig_lldp_client.OpenconfigLldpApi.__dict__) + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-mac.py b/src/CLI/actioner/sonic-cli-mac.py new file mode 100644 index 0000000000..13e5cb6a0b --- /dev/null +++ b/src/CLI/actioner/sonic-cli-mac.py @@ -0,0 +1,201 @@ +#!/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 +import ast +import openconfig_network_instance_client +from rpipe_utils import pipestr +from openconfig_network_instance_client.rest import ApiException +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) + +# Update with network instance API +def generate_body(func, args): + body = None + if func.__name__ == 'get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries': + keypath = ['default'] + else: + body = {} + + return keypath,body + +def mac_fill_count(mac_entries): + static = dynamic = 0 + for mac_entry in mac_entries: + if mac_entry['state']['entry_type'] == 'STATIC': + static += 1 + else: + dynamic += 1 + + mac_entry_table = {'vlan-mac': len(mac_entries), + 'static-mac': static, + 'dynamic-mac': dynamic, + 'total-mac': (static + dynamic) + } + return mac_entry_table + +def fill_mac_info(mac_entry): + mac_entry_table = {'Vlan':mac_entry['vlan'], + 'mac-address':mac_entry['mac_address'], + 'entry-type': mac_entry['state']['entry_type'], + 'port': mac_entry['interface'] + ['interface_ref']['state']['interface'] + } + return mac_entry_table + + +def run(func, args): + + c = openconfig_network_instance_client.Configuration() + c.verify_ssl = False + aa = openconfig_network_instance_client.OpenconfigNetworkInstanceApi(api_client=openconfig_network_instance_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa, func.__name__)(*keypath, body=body) + + else: + api_response = getattr(aa,func.__name__)(*keypath) + + if api_response is None: + print ("Success") + else: + response = api_response.to_dict() + + mac_entries = response['openconfig_network_instanceentries']['entry'] + mac_table_list = [] + if func.__name__ == 'get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries': + if args[1] == 'show': #### -- show mac address table --- ### + for mac_entry in mac_entries: + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[1] == 'mac-addr': #### -- show mac address table [address ]--- ### + for mac_entry in mac_entries: + if args[2] == mac_entry['mac_address']: + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[1] == 'vlan': #### -- show mac address table [vlan ]--- ### + for mac_entry in mac_entries: + if (int(args[2]) == mac_entry['vlan']): + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[1] == 'interface': #### -- show mac address table [interface {Ethernet | Portchannel }]--- ### + for mac_entry in mac_entries: + if args[2] == mac_entry['interface']['interface_ref']['state']['interface']: + mac_table_list.append(fill_mac_info(mac_entry)) + + #### -- show mac address table [static {address | vlan | interface {Ethernet | Portchannel }}]--- ### + elif args[1] == 'static': + if args[2] == 'address': + for mac_entry in mac_entries: + if args[3] == mac_entry['mac_address'] and mac_entry['state']['entry_type'] == 'STATIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[2] == 'vlan': + for mac_entry in mac_entries: + if (int(args[3]) == mac_entry['vlan']) and mac_entry['state']['entry_type'] == 'STATIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[2] == 'interface': + for mac_entry in mac_entries: + if args[3] == mac_entry['interface']['interface_ref']['state']['interface'] and mac_entry['state']['entry_type'] == 'STATIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + else: + for mac_entry in mac_entries: + if mac_entry['state']['entry_type'] == 'STATIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + #### -- show mac address table [dynamic {address | vlan | interface {Ethernet | Portchannel }}]--- ### + elif args[1] == 'dynamic': + if args[2] == 'address': + for mac_entry in mac_entries: + if args[3] == mac_entry['mac_address'] and mac_entry['state']['entry_type'] == 'DYNAMIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[2] == 'vlan': + for mac_entry in mac_entries: + if (int(args[3]) == mac_entry['vlan']) and mac_entry['state']['entry_type'] == 'DYNAMIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + elif args[2] == 'interface': + for mac_entry in mac_entries: + if args[3] == mac_entry['interface']['interface_ref']['state']['interface'] and mac_entry['state']['entry_type'] == 'DYNAMIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + else: + for mac_entry in mac_entries: + if mac_entry['state']['entry_type'] == 'DYNAMIC': + mac_table_list.append(fill_mac_info(mac_entry)) + + + elif args[1] == 'count': #### -- show mac address table count --- ### + mac_table_list.append(mac_fill_count(mac_entries)) + show_cli_output(args[0], mac_table_list) + return + + except ApiException as e: + #print("Exception when calling OpenconfigInterfacesApi->%s : %s\n" %(func.__name__, e)) + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Transaction Failure" + return + print "%Error: Transaction Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + func = eval(sys.argv[1], globals(), openconfig_network_instance_client.OpenconfigNetworkInstanceApi.__dict__) + + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-mclag.py b/src/CLI/actioner/sonic-cli-mclag.py new file mode 100755 index 0000000000..4209583c4f --- /dev/null +++ b/src/CLI/actioner/sonic-cli-mclag.py @@ -0,0 +1,174 @@ +#!/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 json +import collections +import re +import cli_client as cc +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output + + +def invoke(func, args): + body = None + aa = cc.ApiClient() + + ####################################### + # Configure MCLAG Domain Table - START + ####################################### + + #[un]configure local IP Address + if (func == 'patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_source_ip' or + func == 'delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_source_ip'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_DOMAIN/MCLAG_DOMAIN_LIST={domain_id}/source_ip', domain_id=args[0]) + + if (func.startswith("patch") is True): + body = { + "sonic-mclag:source_ip": args[1] + } + return aa.patch(keypath, body) + else: + return aa.delete(keypath) + + #[un]configure Peer IP Address + if (func == 'patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_ip' or + func == 'delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_ip'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_DOMAIN/MCLAG_DOMAIN_LIST={domain_id}/peer_ip', domain_id=args[0]) + + if (func.startswith("patch") is True): + body = { + "sonic-mclag:peer_ip": args[1] + } + return aa.patch(keypath, body) + else: + return aa.delete(keypath) + + #[un]configure Peer Link + if (func == 'patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_link' or + func == 'delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_link'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_DOMAIN/MCLAG_DOMAIN_LIST={domain_id}/peer_link', domain_id=args[0]) + + if (func.startswith("patch") is True): + body = { + "sonic-mclag:peer_link": args[1] + } + return aa.patch(keypath, body) + else: + return aa.delete(keypath) + + #[un]configure Keepalive interval + if (func == 'patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_keepalive_interval' or + func == 'delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_keepalive_interval'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_DOMAIN/MCLAG_DOMAIN_LIST={domain_id}/keepalive_interval', domain_id=args[0]) + + if (func.startswith("patch") is True): + body = { + "sonic-mclag:keepalive_interval": int(args[1]) + } + return aa.patch(keypath, body) + else: + return aa.delete(keypath) + + #configure session Timeout + if (func == 'patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_session_timeout' or + func == 'delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_session_timeout'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_DOMAIN/MCLAG_DOMAIN_LIST={domain_id}/session_timeout', domain_id=args[0]) + + if (func.startswith("patch") is True): + body = { + "sonic-mclag:session_timeout": int(args[1]) + } + return aa.patch(keypath, body) + else: + return aa.delete(keypath) + + #delete MCLAG Domain + if (func == 'delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_DOMAIN/MCLAG_DOMAIN_LIST={domain_id}', domain_id=args[0]) + return aa.delete(keypath) + + + + ####################################### + # Configure MCLAG Domain Table - END + ####################################### + + + ####################################### + # Configure MCLAG Interface Table - START + ####################################### + if (func == 'patch_sonic_mclag_sonic_mclag_mclag_interface_mclag_interface_list'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_INTERFACE/MCLAG_INTERFACE_LIST={domain_id},{if_name}', + domain_id=args[0], if_name=args[1]) + body = { + "sonic-mclag:MCLAG_INTERFACE_LIST": [ + { + "domain_id":int(args[0]), + "if_name":args[1], + "if_type":"PortChannel" + } + ] + } + return aa.patch(keypath, body) + + if (func == 'delete_sonic_mclag_sonic_mclag_mclag_interface_mclag_interface_list'): + keypath = cc.Path('/restconf/data/sonic-mclag:sonic-mclag/MCLAG_INTERFACE/MCLAG_INTERFACE_LIST={domain_id},{if_name}', + domain_id=(args[0]), if_name=args[1]) + return aa.delete(keypath) + + ####################################### + # Configure MCLAG Domain Table - END + ####################################### + + + else: + print("%Error: not implemented") + exit(1) + +def run(func, args): + try: + api_response = invoke(func, args) + + if api_response.ok(): + print "ok...." + response = api_response.content + if response is None: + print "Success" + elif 'sonic-mclag:sonic-mclag' in response.keys(): + value = response['sonic-mclag:sonic-mclag'] + if value is None: + print("Success") + else: + print ("Failed") + + else: + #error response + print api_response.error_message() + + except: + # system/network error + print "%Error: Transaction Failure" + + +if __name__ == '__main__': + pipestr().write(sys.argv) + #pdb.set_trace() + run(sys.argv[1], sys.argv[2:]) + diff --git a/src/CLI/actioner/sonic-cli-mirror.py b/src/CLI/actioner/sonic-cli-mirror.py new file mode 100755 index 0000000000..c8cad78261 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-mirror.py @@ -0,0 +1,184 @@ +#!/usr/bin/python + +############################################################################ +# +# mirror is a tool for handling MIRROR commands. +# +############################################################################ + +import argparse +import getopt +import json +import os +import re +import sys +import swsssdk +from swsssdk import ConfigDBConnector +from scripts.render_cli import show_cli_output +from os import path + +CFG_MIRROR_SESSION_TABLE = "MIRROR_SESSION" +STATE_MIRROR_SESSION_TABLE = "MIRROR_SESSION_TABLE" + +def show_session(session_name): + """ + Show mirror session configuration. Temporary implementation for now. will be modified to Jinja files in next commit. + :param session_name: Optional. Mirror session name. Filter sessions by specified name. + :return: + """ + configdb = ConfigDBConnector() + configdb.connect() + statedb = swsssdk.SonicV2Connector(host='127.0.0.1') + statedb.connect(statedb.STATE_DB) + sessions_db_info = configdb.get_table(CFG_MIRROR_SESSION_TABLE) + for key in sessions_db_info.keys(): + state_db_info = statedb.get_all(statedb.STATE_DB, "{}|{}".format(STATE_MIRROR_SESSION_TABLE, key)) + if state_db_info: + status = state_db_info.get("status", "inactive") + else: + status = "error" + sessions_db_info[key]["status"] = status + erspan_header = ("Name", "Status", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue", + "Policer", "SRC Port", "Direction") + span_header = ("Name", "Status", "DST Port", "SRC Port", "Direction") + + erspan_data = [] + span_data = [] + if session_name is None: + print("\nERSPAN Sessions") + print("---------------------------------------------------------------------------------------------------------") + print("%10s %6s %16s %16s %6s %6s %6s %6s %6s %12s %6s" %("Name", "Status", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue", "Policer", "SRC Port", "Direction")) + + for key, val in sessions_db_info.iteritems(): + if session_name and key != session_name: + continue + + if "src_ip" in val: + if session_name and key == session_name: + print("\nERSPAN Sessions") + print("---------------------------------------------------------------------------------------------------------") + print("%10s %6s %16s %16s %6s %6s %6s %6s %6s %12s %6s" %("Name", "Status", "SRC IP", "DST IP", "GRE", "DSCP", "TTL", "Queue", "Policer", "SRC Port", "Direction")) + print("%10s %6s %16s %16s %6s %6s %6s %6s %6s %12s %6s" %(key, val.get("status", ""), val.get("src_ip", ""), val.get("dst_ip", ""), val.get("gre_type", ""), val.get("dscp", ""), + val.get("ttl", ""), val.get("queue", ""), val.get("policer", ""), + val.get("src_port", ""), val.get("direction", ""))) + if session_name is None: + print("\nSPAN Sessions") + print("---------------------------------------------------------------------------------------------------------") + print("%10s %6s %16s %16s %6s" %("Name", "Status", "DST Port", "SRC Port", "Direction")) + for key, val in sessions_db_info.iteritems(): + if session_name and key != session_name: + continue + if "dst_port" in val: + if session_name and key == session_name: + print("\nSPAN Sessions") + print("---------------------------------------------------------------------------------------------------------") + print("%10s %6s %16s %16s %6s" %("Name", "Status", "DST Port", "SRC Port", "Direction")) + print("%10s %6s %16s %16s %6s" %(key, val.get("status", ""), val.get("dst_port", ""), val.get("src_port", ""), val.get("direction", ""))) + + +def session(session_name): + """ + Show mirror session configuration. + :return: + """ + show_session(session_name) + +def show_mirror(args): + """ + Add port mirror session + """ + session(args.session) + +def config_span(args): + """ + Add port mirror session + """ + config_db = ConfigDBConnector() + config_db.connect() + + session_info = { + } + + if args.destination is not None: + session_info['dst_port'] = args.destination + + if args.source is not None: + session_info['src_port'] = args.source + + if args.direction is not None: + session_info['direction'] = args.direction + + if args.dst_ip is not None: + session_info['dst_ip'] = args.dst_ip + + if args.src_ip is not None: + session_info['src_ip'] = args.src_ip + + if args.dscp is not None: + session_info['dscp'] = args.dscp + + if args.ttl is not None: + session_info['ttl'] = args.ttl + + if args.gre is not None: + session_info['gre_type'] = args.gre + + if args.source is not None: + print("sucess. create mirror session " + args.session + " destination " + args.destination + " source " + args.source + " direction " + args.direction) + + if args.dst_ip is not None: + print("sucess. create mirror session " + args.session + " dst_ip " + args.dst_ip + " src_ip " + args.src_ip + " dscp " + args.dscp + " ttl " + args.ttl) + + config_db.set_entry("MIRROR_SESSION", args.session, session_info) + +def remove_span(args): + """ + Delete mirror session + """ + config_db = ConfigDBConnector() + config_db.connect() + + print("sucess. remove mirror session " + args.session) + config_db.set_entry("MIRROR_SESSION", args.session, None) + +def main(): + + parser = argparse.ArgumentParser(description='Handles MIRROR commands', + version='1.0.0', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""" +Examples: + mirror -config -deviceid value + mirror -config -collector collectorname -iptype ipv4/ipv6 -ip ipaddr -port value + mirror -clear -device_id + mirror -clear -collector collectorname + mirror -show -device_id + mirror -show -collector collectorname +""") + + parser.add_argument('-clear', '--clear', action='store_true', help='Clear mirror information') + parser.add_argument('-show', '--show', action='store_true', help='Show mirror information') + parser.add_argument('-config', '--config', action='store_true', help='Config mirror information') + parser.add_argument('-session', '--session', type=str, help='mirror session name') + parser.add_argument('-destination', '--destination', help='destination port') + parser.add_argument('-source', '--source', type=str, help='mirror source port') + parser.add_argument('-direction', '--direction', type=str, help='mirror direction') + parser.add_argument('-dst_ip', '--dst_ip', help='ERSPAN destination ip address') + parser.add_argument('-src_ip', '--src_ip', help='ERSPAN source ip address') + parser.add_argument('-dscp', '--dscp', help='ERSPAN dscp') + parser.add_argument('-gre', '--gre', help='ERSPAN gre') + parser.add_argument('-ttl', '--ttl', help='ERSPAN ttl') + + args = parser.parse_args() + + if args.config: + config_span(args) + elif args.clear: + remove_span(args) + elif args.show: + show_mirror(args) + + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/src/CLI/actioner/sonic-cli-pfm.py b/src/CLI/actioner/sonic-cli-pfm.py new file mode 100644 index 0000000000..155e404961 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-pfm.py @@ -0,0 +1,128 @@ +#!/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 +import ast +import openconfig_platform_client +from rpipe_utils import pipestr +from openconfig_platform_client.rest import ApiException +from scripts.render_cli import show_cli_output + + +import urllib3 +urllib3.disable_warnings() + +blocked_fields = {'parent':0, 'used_power':0, 'allocated_power':0, 'temperature':0} +plugins = dict() + +def filter_json_value(value): + for key,val in value.items(): + if key in blocked_fields: + del value[key] + else: + temp = key.split('_') + alt_key = '' + for i in temp: + alt_key = alt_key + i.capitalize() + ' ' + value[alt_key]=value.pop(key) + + return value + +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 generate_body(func, args): + body = None + # Get the rules of all ACL table entries. + + if func.__name__ == 'get_openconfig_platform_components': + keypath = [] + + else: + body = {} + + return keypath,body + + +def run(func, args): + c = openconfig_platform_client.Configuration() + c.verify_ssl = False + aa = openconfig_platform_client.OpenconfigPlatformApi(api_client=openconfig_platform_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + + else : + api_response = getattr(aa,func.__name__)(*keypath) + + if api_response is None: + print ("Success") + else: + api_response = aa.api_client.sanitize_for_serialization(api_response) + value = api_response['openconfig-platform:components']['component'][0]['state'] + if value is None: + return + if 'oper-status' in value: + temp = value['oper-status'].split(':') + if temp[len(temp) - 1] is not None: + value['oper-status'] = temp[len(temp) - 1] + show_cli_output(sys.argv[2],filter_json_value(value)) + + except ApiException as e: + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Application Failure" + return + print "%Error: Application Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + #pdb.set_trace() + func = eval(sys.argv[1], globals(), openconfig_platform_client.OpenconfigPlatformApi.__dict__) + run(func, sys.argv[2:]) + diff --git a/src/CLI/actioner/sonic-cli-portchannel.py b/src/CLI/actioner/sonic-cli-portchannel.py new file mode 100644 index 0000000000..49b7607e79 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-portchannel.py @@ -0,0 +1,90 @@ +#!/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 +import ast +import collections +from rpipe_utils import pipestr +import cli_client as cc +from scripts.render_cli import show_cli_output + +pcDict = {} +memberDict = {} + +def invoke_api(func, args=[]): + api = cc.ApiClient() + + if func == 'get_sonic_portchannel_sonic_portchannel_lag_table': + path = cc.Path('/restconf/data/sonic-portchannel:sonic-portchannel/LAG_TABLE') + return api.get(path) + + if func == 'get_sonic_portchannel_sonic_portchannel_lag_member_table': + path = cc.Path('/restconf/data/sonic-portchannel:sonic-portchannel/LAG_MEMBER_TABLE') + return api.get(path) + + return api.cli_not_implemented(func) + +def run(func, args): + response = invoke_api(func, args) + + if response.ok(): + if response.content is not None: + # Get Command Output + api_response = response.content + laglst =[] + if 'sonic-portchannel:LAG_TABLE' in api_response: + value = api_response['sonic-portchannel:LAG_TABLE'] + if 'LAG_TABLE_LIST' in value: + laglst = value['LAG_TABLE_LIST'] + if api_response is None: + print("Failed") + else: + if func == 'get_sonic_portchannel_sonic_portchannel_lag_table': + memlst=[] + # Get members for all PortChannels + memebrs_resp = invoke_api('get_sonic_portchannel_sonic_portchannel_lag_member_table') + if not memebrs_resp.ok(): + print memebrs_resp.error_message() + return + + api_response_members = memebrs_resp.content + + if 'sonic-portchannel:LAG_MEMBER_TABLE' in api_response_members: + memlst = api_response_members['sonic-portchannel:LAG_MEMBER_TABLE']['LAG_MEMBER_TABLE_LIST'] + for pc_dict in laglst: + pc_dict['members']=[] + pc_dict['type']="Eth" + for mem_dict in memlst: + if mem_dict['name'] == pc_dict['lagname']: + keypath = [mem_dict['ifname']] + pc_dict['members'].append(mem_dict['ifname']) + show_cli_output(args[0], laglst) + else: + return + else: + print response.error_message() + +if __name__ == '__main__': + + pipestr().write(sys.argv) + func = sys.argv[1] + + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-ptp.py b/src/CLI/actioner/sonic-cli-ptp.py new file mode 100644 index 0000000000..a939ae060d --- /dev/null +++ b/src/CLI/actioner/sonic-cli-ptp.py @@ -0,0 +1,196 @@ +#!/usr/bin/python + +import sys +import swsssdk +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output +from swsssdk import ConfigDBConnector + +import urllib3 +urllib3.disable_warnings() + +PTP_CLOCK = 'PTP_CLOCK' +PTP_PORT = 'PTP_PORT|GLOBAL' +PTP_GLOBAL = 'GLOBAL' + + +def port_state_to_str(state_num): + outval = "" + if state_num == "1": + outval = "initializing" + if state_num == "2": + outval = "faulty" + if state_num == "3": + outval = "disabled" + if state_num == "4": + outval = "listening" + if state_num == "5": + outval = "pre_master" + if state_num == "6": + outval = "master" + if state_num == "7": + outval = "passive" + if state_num == "8": + outval = "uncalibrated" + if state_num == "9": + outval = "slave" + + return outval + + +if __name__ == '__main__': + pipestr().write(sys.argv) + db = swsssdk.SonicV2Connector(host='127.0.0.1') + db.connect(db.STATE_DB) + + config_db = ConfigDBConnector() + if config_db is None: + sys.exit() + config_db.connect() + if sys.argv[1] == 'get_ietf_ptp_ptp_instance_list_default_ds': + raw_data = db.get_all(db.STATE_DB, "PTP_CLOCK|GLOBAL") + if not raw_data: + raw_data = config_db.get_entry(PTP_CLOCK, PTP_GLOBAL) + api_response = {} + api_response['ietf-ptp:default-ds'] = raw_data + else: + api_response = {} + api_inner_response = {} + api_clock_quality_response = {} + + for key, val in raw_data.items(): + if key == "clock-class" or key == "clock-accuracy" or key == "offset-scaled-log-variance": + api_clock_quality_response[key] = val + else: + api_inner_response[key] = val + + api_inner_response["clock-quality"] = api_clock_quality_response + api_response['ietf-ptp:default-ds'] = api_inner_response + + show_cli_output(sys.argv[3], api_response) + elif sys.argv[1] == 'get_ietf_ptp_ptp_instance_list_time_properties_ds': + raw_data = db.get_all(db.STATE_DB, "PTP_TIMEPROPDS|GLOBAL") + if not raw_data: + sys.exit() + api_response = {} + api_inner_response = {} + + for key, val in raw_data.items(): + if key == "time-traceable" or key == "frequency-traceable" or key == "ptp-timescale" or key == "leap59" or key == "leap61" or key == "current-utc-offset-valid": + if val == "0": + val = "false" + else: + val = "true" + api_inner_response[key] = val + + api_response['ietf-ptp:time-properties-ds'] = api_inner_response + show_cli_output(sys.argv[3], api_response) + + sys.exit() + elif sys.argv[1] == 'get_ietf_ptp_ptp_instance_list_parent_ds': + raw_data = db.get_all(db.STATE_DB, "PTP_PARENTDS|GLOBAL") + if not raw_data: + sys.exit() + api_response = {} + api_inner_response = {} + api_parent_id_response = {} + api_gm_response = {} + + for key, val in raw_data.items(): + if key == "parent-stats": + if val == "0": + val = "false" + else: + val = "true" + if key == "clock-identity" or key == "port-number": + api_parent_id_response[key] = val + elif key == "clock-class" or key == "clock-accuracy" or key == "offset-scaled-log-variance": + api_gm_response[key] = val + else: + api_inner_response[key] = val + + api_inner_response["parent-port-identity"] = api_parent_id_response + api_inner_response["grandmaster-clock-quality"] = api_gm_response + api_response['ietf-ptp:parent-ds'] = api_inner_response + + show_cli_output(sys.argv[3], api_response) + + sys.exit() + elif sys.argv[1] == 'get_ietf_ptp_ptp_instance_list_port_ds_list': + raw_data = db.get_all(db.STATE_DB, "PTP_PORT|GLOBAL|Ethernet" + sys.argv[3]) + if not raw_data: + sys.exit() + api_response = {} + api_response_list = [] + api_inner_response = {} + + for key, val in raw_data.items(): + if key == "port-state": + val = port_state_to_str(val) + if key == "delay-mechanism": + if val == "1": + val = "e2e" + if val == "2": + val = "p2p" + + api_inner_response[key] = val + + api_response_list.append(api_inner_response) + api_response['ietf-ptp:port-ds-list'] = api_response_list + + show_cli_output(sys.argv[4], api_response) + sys.exit() + elif sys.argv[1] == 'get_ietf_ptp_ptp_instance_list': + raw_data = db.keys(db.STATE_DB, "PTP_PORT|GLOBAL|*") + if not raw_data: + sys.exit() + api_response = {} + api_response_list = [] + port_ds_dict = {} + port_ds_list = [] + port_ds_entry = {} + for key in raw_data: + port_ds_entry = {} + port_ds_entry["port-number"] = key.replace("PTP_PORT|GLOBAL|", "") + state_data = db.get_all(db.STATE_DB, key) + + port_ds_entry["port-state"] = port_state_to_str(state_data["port-state"]) + port_ds_list.append(port_ds_entry) + port_ds_dict['port-ds-list'] = port_ds_list + api_response_list.append(port_ds_dict) + api_response['ietf-ptp:instance_list'] = api_response_list + show_cli_output(sys.argv[3], api_response) + elif sys.argv[1] == 'patch_ietf_ptp_ptp_instance_list_default_ds_domain_number': + data = {} + data['domain-number'] = sys.argv[3] + config_db.mod_entry(PTP_CLOCK, PTP_GLOBAL, data) + elif sys.argv[1] == 'patch_ietf_ptp_ptp_instance_list_default_ds_priority1': + data = {} + data['priority1'] = sys.argv[3] + config_db.mod_entry(PTP_CLOCK, PTP_GLOBAL, data) + elif sys.argv[1] == 'patch_ietf_ptp_ptp_instance_list_default_ds_priority2': + data = {} + data['priority2'] = sys.argv[3] + config_db.mod_entry(PTP_CLOCK, PTP_GLOBAL, data) + elif sys.argv[1] == 'patch_ietf_ptp_ptp_instance_list_default_ds_two_step_flag': + data = {} + if sys.argv[3] == "enable": + data['two-step-flag'] = '1' + else: + data['two-step-flag'] = '0' + config_db.mod_entry(PTP_CLOCK, PTP_GLOBAL, data) + elif sys.argv[1] == 'patch_ietf_ptp_ptp_transparent_clock_default_ds_delay_mechanism': + data = {} + data['tc-delay-mechanism'] = sys.argv[2] + config_db.mod_entry(PTP_CLOCK, PTP_GLOBAL, data) + elif sys.argv[1] == 'add_port': + data = {} + data['enable'] = '1' + config_db.set_entry(PTP_PORT, sys.argv[2], data) + elif sys.argv[1] == 'del_port': + config_db.set_entry(PTP_PORT, sys.argv[2], None) + else: + data = {} + data[sys.argv[1]] = sys.argv[2] + config_db.mod_entry(PTP_CLOCK, PTP_GLOBAL, data) + db.close(db.STATE_DB) diff --git a/src/CLI/actioner/sonic-cli-sflow.py b/src/CLI/actioner/sonic-cli-sflow.py new file mode 100755 index 0000000000..6758fe7096 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-sflow.py @@ -0,0 +1,216 @@ +#!/usr/bin/python +import sys +import time +import json +import ast +from rpipe_utils import pipestr +from collections import OrderedDict +from scripts.render_cli import show_cli_output +import sonic_sflow_client +from sonic_sflow_client.api.sonic_sflow_api import SonicSflowApi +from sonic_sflow_client.rest import ApiException + + +import urllib3 +urllib3.disable_warnings() +plugins = dict() + +SFLOW_DEFAULT_PORT = 6343 + +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_admin_state(resp): + if 'sonic-sflow:sonic-sflow' not in resp: + return 'down' + resp = resp['sonic-sflow:sonic-sflow'] + if ('SFLOW' in resp and + 'SFLOW_LIST' in resp['SFLOW'] and + 'admin_state' in resp['SFLOW']['SFLOW_LIST'][0]): + return resp['SFLOW']['SFLOW_LIST'][0]['admin_state'] + return 'down' + +def get_sflow_polling_interval(resp): + if 'sonic-sflow:sonic-sflow' not in resp: + return 'default' + resp = resp['sonic-sflow:sonic-sflow'] + if ('SFLOW' in resp and + 'SFLOW_LIST' in resp['SFLOW'] and + 'polling_interval' in resp['SFLOW']['SFLOW_LIST'][0]): + return resp['SFLOW']['SFLOW_LIST'][0]['polling_interval'] + return 'default' + +def get_sflow_agent_id(resp): + if 'sonic-sflow:sonic-sflow' not in resp: + return 'default' + resp = resp['sonic-sflow:sonic-sflow'] + if ('SFLOW' in resp and + 'SFLOW_LIST' in resp['SFLOW'] and + 'agent_id' in resp['SFLOW']['SFLOW_LIST'][0]): + return resp['SFLOW']['SFLOW_LIST'][0]['agent_id'] + return 'default' + +def get_collector_list(resp): + if 'sonic-sflow:sonic-sflow' not in resp: + return [] + resp = resp['sonic-sflow:sonic-sflow'] + if ('SFLOW_COLLECTOR' in resp and + 'SFLOW_COLLECTOR_LIST' in resp['SFLOW_COLLECTOR']): + return resp['SFLOW_COLLECTOR']['SFLOW_COLLECTOR_LIST'] + return [] + +def get_session_list(resp, table_name): + if ('sonic-sflow:sonic-sflow' in resp and + table_name in resp['sonic-sflow:sonic-sflow'] and + 'SFLOW_SESSION_LIST' in resp['sonic-sflow:sonic-sflow'][table_name]): + return resp['sonic-sflow:sonic-sflow'][table_name]['SFLOW_SESSION_LIST'] + return [] + +def get_all_sflow_info(aa): + keypath = [] + resp = getattr(aa, 'get_sonic_sflow_sonic_sflow')(*keypath) + resp = aa.api_client.sanitize_for_serialization(resp) + return resp + +def get_all_sflow_sess_info(aa): + keypath = [] + resp = getattr(aa, 'get_sonic_sflow_sonic_sflow_sflow_session')(*keypath) + resp = aa.api_client.sanitize_for_serialization(resp) + return resp + +def generate_body(func, args): + body = None + keypath = [] + port = SFLOW_DEFAULT_PORT + if len(args) == 3: + port = int(args[2]) + if func.__name__ == 'put_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list': + keypath = [args[0]] + body = { "sonic-sflow:SFLOW_COLLECTOR_LIST": [ + { + "collector_name": args[0], + "collector_ip": args[1], + "collector_port": port + }] } + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list': + keypath = [args[0]] + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_sample_rate': + keypath = [args[0]] + body = { + "sonic-sflow:sample_rate": int(args[1]) + } + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_sample_rate': + keypath = [args[0]] + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_admin_state': + keypath = [args[0]] + body = { + "sonic-sflow:admin_state": args[1] + } + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_sflow_list_admin_state': + keypath = ['global'] + body = { + "sonic-sflow:admin_state": args[0] + } + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id': + keypath = ['global'] + body = { + "sonic-sflow:agent_id": args[0] + } + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval': + keypath = ['global'] + body = { + "sonic-sflow:polling_interval": int(args[0]) + } + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval': + keypath = ['global'] + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id': + keypath = ['global'] + + return keypath, body; + +def print_exception(e): + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errDict = {} + for dict in err["error"]: + for k, v in dict.iteritems(): + errDict[k] = v + if "error-message" in errDict: + print("Error: " + errDict["error-message"]) + return + print("failed") + return + +def run(func, args): + try: + c = sonic_sflow_client.Configuration() + c.verify_ssl = False + aa = sonic_sflow_client.SonicSflowApi(api_client=sonic_sflow_client.ApiClient(configuration=c)) + + resp = get_all_sflow_info(aa) + cresp = None + + if resp is None: + print("Can't get sFlow information") + return + + # sFlow show commands + if func.__name__ == 'get_sonic_sflow_sonic_sflow': + sflow_info = {'sflow' : { 'admin_state' : get_sflow_admin_state(resp), 'polling_interval' : get_sflow_polling_interval(resp), + 'agent_id' : get_sflow_agent_id(resp)}} + sflow_col_lst = get_collector_list(resp) + sflow_info['col_info'] = {} + sflow_info['col_info']['col_cnt'] = len(sflow_col_lst) + sflow_info['col_info']['col_lst'] = sflow_col_lst + show_cli_output(sys.argv[2], sflow_info) + return + elif func.__name__ == 'get_sonic_sflow_sonic_sflow_sflow_session_table': + sess_lst = get_session_list(resp, 'SFLOW_SESSION_TABLE') + show_cli_output(sys.argv[2], sess_lst) + return + + # sFlow collector config commands + keypath, body = generate_body(func, args) + if func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_sflow_list_admin_state': + cresp = getattr(aa, func.__name__)(*keypath, body=body) + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id': + cresp = getattr(aa, func.__name__)(*keypath, body=body) + elif func.__name__ == 'put_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list': + cresp = getattr(aa, func.__name__)(*keypath, body=body) + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval': + cresp = getattr(aa, func.__name__)(*keypath, body=body) + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list': + name = args[0] + sflow_col_lst = get_collector_list(resp) + for col in sflow_col_lst: + if name in col['collector_name']: + cresp = getattr(aa,func.__name__)(*keypath) + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval': + cresp = getattr(aa,func.__name__)(*keypath) + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id': + cresp = getattr(aa,func.__name__)(*keypath) + + # sFlow session config commands + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_admin_state': + cresp = getattr(aa, func.__name__)(*keypath, body = body) + elif func.__name__ == 'patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_sample_rate': + cresp = getattr(aa, func.__name__)(*keypath, body = body) + elif func.__name__ == 'delete_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_sample_rate': + cresp = getattr(aa, func.__name__)(*keypath) + except ApiException as e: + print_exception(e) + return + +if __name__ == '__main__': + pipestr().write(sys.argv) + func = eval(sys.argv[1], globals(), sonic_sflow_client.SonicSflowApi.__dict__) + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-stp.py b/src/CLI/actioner/sonic-cli-stp.py new file mode 100644 index 0000000000..51ad5af8c4 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-stp.py @@ -0,0 +1,372 @@ +#!/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 json +import openconfig_spanning_tree_client +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output +from openconfig_spanning_tree_client.rest import ApiException +import urllib3 + +urllib3.disable_warnings() + +cust_to_oc_URI = { + "PVST":{ + 'get_custom_stp': 'get_openconfig_spanning_tree_ext_stp_pvst', + 'get_custom_stp_vlan': 'get_openconfig_spanning_tree_ext_stp_pvst_vlan', + 'get_custom_stp_vlan_interfaces_interface': + 'get_openconfig_spanning_tree_ext_stp_pvst_vlan', + 'get_custom_stp_counters': 'get_openconfig_spanning_tree_ext_stp_pvst', + 'get_custom_stp_counters_vlan': 'get_openconfig_spanning_tree_ext_stp_pvst_vlan', + 'get_custom_stp_inconsistentports': 'get_openconfig_spanning_tree_ext_stp_pvst', + 'get_custom_stp_inconsistentports_vlan': 'get_openconfig_spanning_tree_ext_stp_pvst_vlan', + 'patch_custom_stp_vlan_config_enable': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_spanning_tree_enable', + 'patch_custom_stp_vlan_config_forwarding_delay': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_forwarding_delay', + 'patch_custom_stp_vlan_config_hello_time': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_hello_time', + 'patch_custom_stp_vlan_config_max_age': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_max_age', + 'patch_custom_stp_vlan_config_bridge_priority': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_bridge_priority', + 'delete_custom_stp_vlan_config_forwarding_delay': + 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_forwarding_delay', + 'delete_custom_stp_vlan_config_hello_time': + 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_hello_time', + 'delete_custom_stp_vlan_config_max_age': + 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_max_age', + 'delete_custom_stp_vlan_config_bridge_priority': + 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_bridge_priority', + 'patch_custom_stp_vlan_interfaces_interface_config_cost': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_cost', + 'patch_custom_stp_vlan_interfaces_interface_config_port_priority': + 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_port_priority', + 'delete_custom_stp_vlan_interfaces_interface_config_cost': + 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_cost', + 'delete_custom_stp_vlan_interfaces_interface_config_port_priority': + 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_port_priority' + '' + }, + "RAPID_PVST":{ + 'get_custom_stp': 'get_openconfig_spanning_tree_stp_rapid_pvst', + 'get_custom_stp_vlan': 'get_openconfig_spanning_tree_stp_rapid_pvst_vlan', + 'get_custom_stp_vlan_interfaces_interface': + 'get_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface', + 'get_custom_stp_counters': 'get_openconfig_spanning_tree_stp_rapid_pvst', + 'get_custom_stp_counters_vlan': 'get_openconfig_spanning_tree_stp_rapid_pvst_vlan', + 'get_custom_stp_inconsistentports': 'get_openconfig_spanning_tree_stp_rapid_pvst', + 'get_custom_stp_inconsistentports_vlan': 'get_openconfig_spanning_tree_stp_rapi_pvst_vlan', + 'patch_custom_stp_vlan_config_enable': + 'patch_openconfig_spanning_tree_ext_stp_rapid_pvst_vlan_config_spanning_tree_enable', + 'patch_custom_stp_vlan_config_forwarding_delay': + 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_forwarding_delay', + 'patch_custom_stp_vlan_config_hello_time': + 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_hello_time', + 'patch_custom_stp_vlan_config_max_age': + 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_max_age', + 'patch_custom_stp_vlan_config_bridge_priority': + 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_bridge_priority', + 'delete_custom_stp_vlan_config_forwarding_delay': + 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_forwarding_delay', + 'delete_custom_stp_vlan_config_hello_time': + 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_hello_time', + 'delete_custom_stp_vlan_config_max_age': + 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_max_age', + 'delete_custom_stp_vlan_config_bridge_priority': + 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_bridge_priority', + 'patch_custom_stp_vlan_interfaces_interface_config_cost': + 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_cost', + 'patch_custom_stp_vlan_interfaces_interface_config_port_priority': + 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_port_priority', + 'delete_custom_stp_vlan_interfaces_interface_config_cost': + 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_cost', + 'delete_custom_stp_vlan_interfaces_interface_config_port_priority': + 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_port_priority' + } +} + +def generate_body(func, args): + body = None + keypath = [] + + if func.__name__ == 'get_openconfig_spanning_tree_stp_rapid_pvst' or func.__name__ == 'get_openconfig_spanning_tree_ext_stp_pvst': + keypath = [] + elif func.__name__ == 'get_openconfig_spanning_tree_stp_rapid_pvst_vlan' or func.__name__ == 'get_openconfig_spanning_tree_ext_stp_pvst_vlan': + if (len(args) > 2): + keypath = [ int(args[2]) ] + elif func.__name__ == 'get_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface' or func.__name__ == 'get_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface': + if (len(args) > 2): + keypath = [ int(args[2]), args[3] ] + elif func.__name__ == 'get_openconfig_spanning_tree_stp_interfaces': + keypath = [] + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_spanning_tree_enable' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_rapid_pvst_vlan_config_spanning_tree_enable': + keypath = [ int(args[1]) ] + if (len(args) > 2): + if args[2] == "True": + body = { "openconfig-spanning-tree-ext:spanning-tree-enable": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree-ext:spanning-tree-enable": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_hello_time' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_hello_time': + keypath = [ int(args[1]) ] + body = { "openconfig-spanning-tree:hello-time": int(args[2]) } + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_hello_time' or func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_hello_time': + keypath = [ int(args[1]) ] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_forwarding_delay' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_forwarding_delay': + keypath = [ int(args[1]) ] + body = { "openconfig-spanning-tree:forwarding-delay": int(args[2]) } + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_forwarding_delay' or func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_forwarding_delay': + keypath = [ int(args[1]) ] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_max_age' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_max_age': + keypath = [ int(args[1]) ] + body = { "openconfig-spanning-tree:max-age": int(args[2]) } + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_max_age' or func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_max_age': + keypath = [ int(args[1]) ] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_bridge_priority' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_config_bridge_priority': + keypath = [ int(args[1]) ] + body = { "openconfig-spanning-tree:bridge-priority": int(args[2]) } + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_config_bridge_priority' or func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_config_bridge_priority': + keypath = [ int(args[1]) ] + elif func.__name__ == 'post_openconfig_spanning_tree_stp_global_config_enabled_protocol': + keypath = [] + if (len(args) > 1): + if args[1] == 'pvst': + body = { "openconfig-spanning-tree:enabled-protocol": ['PVST'] } + else: + body = { "openconfig-spanning-tree:enabled-protocol": ['RAPID_PVST'] } + else: + body = { "openconfig-spanning-tree:enabled-protocol": ['PVST'] } + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_global_config_bpdu_filter': + if args[1] == "True": + body = { "openconfig-spanning-tree:bpdu-filter": True } + elif args[1] == "False": + body = { "openconfig-spanning-tree:bpdu-filter": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_spanning_tree_enable': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree-ext:spanning-tree-enable": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree-ext:spanning-tree-enable": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_uplink_fast': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree-ext:uplink-fast": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree-ext:uplink-fast": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_portfast': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree-ext:portfast": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree-ext:portfast": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_filter': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree:bpdu-filter": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree:bpdu-filter": False } + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_filter': + keypath = [args[1]] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_guard': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree:bpdu-guard": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree:bpdu-guard": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_bpdu_guard_port_shutdown': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree-ext:bpdu-guard-port-shutdown": True } + elif args[2] == "False": + body = { "openconfig-spanning-tree-ext:bpdu-guard-port-shutdown": False } + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_interfaces_interface_config_guard': + keypath = [args[1]] + body = { "openconfig-spanning-tree:guard": args[2] } + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_cost': + keypath = [args[1]] + body = { "openconfig-spanning-tree-ext:cost": int(args[2]) } + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_port_priority': + keypath = [args[1]] + body = { "openconfig-spanning-tree-ext:port-priority": int(args[2]) } + elif func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_interfaces_interface_config_cost': + keypath = [args[1]] + elif func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_interfaces_interface_config_port_priority': + keypath = [args[1]] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_interfaces_interface_config_link_type': + keypath = [args[1]] + body = { "openconfig-spanning-tree:link-type": args[2] } + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_interfaces_interface_config_link_type': + keypath = [args[1]] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_interfaces_interface_config_edge_port': + keypath = [args[1]] + if args[2] == "True": + body = { "openconfig-spanning-tree:edge-port": "EDGE_ENABLE" } + elif args[2] == "False": + body = { "openconfig-spanning-tree:edge-port": "EDGE_DISABLE" } + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_cost' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_cost': + keypath = [int(args[1]), args[2]] + body = { "openconfig-spanning-tree:cost": int(args[3])} + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_cost' or func.__name__== 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_cost': + keypath = [int(args[1]), args[2]] + elif func.__name__ == 'patch_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_port_priority' or func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_port_priority': + keypath = [int(args[1]), args[2]] + body = { "openconfig-spanning-tree:port-priority": int(args[3])} + elif func.__name__ == 'delete_openconfig_spanning_tree_stp_rapid_pvst_vlan_interfaces_interface_config_port_priority' or func.__name__ == 'delete_openconfig_spanning_tree_ext_stp_pvst_vlan_interfaces_interface_config_port_priority': + keypath = [int(args[1]), args[2]] + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_global_config_forwarding_delay': + body = {"openconfig-spanning-tree-ext:forwarding-delay": int(args[1])} + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_global_config_hello_time': + body = {"openconfig-spanning-tree-ext:hello-time": int(args[1])} + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_global_config_max_age': + body = {"openconfig-spanning-tree-ext:max-age": int(args[1])} + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_global_config_bridge_priority': + body = {"openconfig-spanning-tree-ext:bridge-priority": int(args[1])} + elif func.__name__ == 'patch_openconfig_spanning_tree_ext_stp_global_config_rootguard_timeout': + body = {"openconfig-spanning-tree-ext:rootguard-timeout": int(args[1])} + + return keypath,body + +def getId(item): + return item['vlan-id'] + +def stp_mode_get(aa): + keypath = [] + stp_mode = None + stp_resp = None + try: + stp_resp = getattr(aa,"get_openconfig_spanning_tree_stp_global_config_enabled_protocol")(*keypath) + if not stp_resp: + print (" Failed to get STP mode") + return stp_resp,stp_mode + + stp_resp = aa.api_client.sanitize_for_serialization(stp_resp) + + if stp_resp['openconfig-spanning-tree:enabled-protocol'][0] == "openconfig-spanning-tree-ext:PVST": + stp_mode = "PVST" + elif stp_resp['openconfig-spanning-tree:enabled-protocol'][0] == "openconfig-spanning-tree-types:RAPID_PVST": + stp_mode = "RAPID_PVST" + + except ApiException as e: + error_body = json.loads(e.body) + if "ietf-restconf:errors" in error_body and 'error' in error_body["ietf-restconf:errors"]: + error = error_body["ietf-restconf:errors"]["error"][0] + if "error-message" in error and error["error-message"]: + print "%Error: "+ error["error-message"] + " or STP not enabled" + return stp_resp,stp_mode + + print "%Error: Transaction Failure" + + return stp_resp,stp_mode + +def run(args): + + c = openconfig_spanning_tree_client.Configuration() + c.verify_ssl = False + aa = openconfig_spanning_tree_client.OpenconfigSpanningTreeApi(api_client=openconfig_spanning_tree_client.ApiClient(configuration=c)) + + oc_func = None + if "custom" in args[0]: + stp_resp, stp_mode = stp_mode_get(aa) + if stp_mode is None: + return; + oc_func = cust_to_oc_URI[stp_mode][args[0]] + else: + oc_func = args[0] + + if oc_func is None: + return + + func = eval(oc_func, globals(), openconfig_spanning_tree_client.OpenconfigSpanningTreeApi.__dict__) + + # create a body block + keypath, body = generate_body(func, args) + api_response = None + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + else : + api_response = getattr(aa,func.__name__)(*keypath) + + api_response = aa.api_client.sanitize_for_serialization(api_response) + + if not api_response: + if "get_" not in func.__name__: + print "Success" + return + else: + if args[0] == 'get_custom_stp' or args[0] == 'get_custom_stp_counters' or args[0] == 'get_custom_stp_inconsistentports': + # Sort based on VLAN id + if stp_mode == "RAPID_PVST": + vlan_list = api_response['openconfig-spanning-tree:rapid-pvst'] + else: + vlan_list = api_response['openconfig-spanning-tree-ext:pvst'] + + if 'vlan' in vlan_list: + tup = vlan_list['vlan'] + vlan_list['vlan'] = sorted(tup, key=getId) + + if args[0] == 'get_custom_stp' or args[0] == 'get_custom_stp_vlan' or args[0] == 'get_custom_stp_vlan_interfaces_interface': + # add stp mode/protocols to the response + api_response.update(stp_resp) + keypath = [] + # add stp interfaces to the response + if args[0] == 'get_custom_stp_vlan_interfaces_interface': + if (len(args) > 2): + keypath = [ args[3] ] + stp_intf_response = getattr(aa,"get_openconfig_spanning_tree_stp_interfaces_interface")(*keypath) + else: + stp_intf_response = getattr(aa,"get_openconfig_spanning_tree_stp_interfaces")(*keypath) + + stp_intf_response = aa.api_client.sanitize_for_serialization(stp_intf_response) + if stp_intf_response: + api_response.update(stp_intf_response) + + elif args[0] == 'get_custom_stp_counters' or args[0] == 'get_custom_stp_counters_vlan': + # add stp mode/protocols to the response + api_response.update(stp_resp) + elif args[0] == 'get_custom_stp_inconsistentports' or args[0] == 'get_custom_stp_inconsistentports_vlan': + # add stp mode/protocols to the response + api_response.update(stp_resp) + keypath = [] + # add stp interfaces to the response + stp_global_response = getattr(aa,"get_openconfig_spanning_tree_stp_global_config")(*keypath) + stp_global_response = aa.api_client.sanitize_for_serialization(stp_global_response) + api_response.update(stp_global_response) + + show_cli_output(args[1], api_response) + return + + except ApiException as e: + error_body = json.loads(e.body) + if "ietf-restconf:errors" in error_body and 'error' in error_body["ietf-restconf:errors"]: + error = error_body["ietf-restconf:errors"]["error"][0] + if "error-message" in error and error["error-message"]: + print "%Error: "+ error["error-message"] + return + + print "%Error: Transaction Failure" + + return + +if __name__ == '__main__': + + pipestr().write(sys.argv) + run(sys.argv[1:]) diff --git a/src/CLI/actioner/sonic-cli-sys.py b/src/CLI/actioner/sonic-cli-sys.py new file mode 100644 index 0000000000..1fbbb7317d --- /dev/null +++ b/src/CLI/actioner/sonic-cli-sys.py @@ -0,0 +1,164 @@ +#:!/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 +import ast +import openconfig_system_client +from rpipe_utils import pipestr +from openconfig_system_client.rest import ApiException +from scripts.render_cli import show_cli_output + + +import urllib3 +urllib3.disable_warnings() + + +plugins = dict() + +def util_capitalize(value): + for key,val in value.items(): + temp = key.split('_') + alt_key = '' + for i in temp: + alt_key = alt_key + i.capitalize() + ' ' + value[alt_key]=value.pop(key) + return value + +def system_state_key_change(value): + value.pop('motd_banner') + value.pop('login_banner') + return util_capitalize(value) + + +def memory_key_change(value): + value['Total']=value.pop('physical') + value['Used']=value.pop('reserved') + return value + +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 generate_body(func, args): + body = None + # Get the rules of all ACL table entries. + + if func.__name__ == 'get_openconfig_system_system_state': + keypath = [] + elif func.__name__ == 'get_openconfig_system_system_clock': + keypath = [] + elif func.__name__ == 'get_openconfig_system_system_memory': + keypath = [] + elif func.__name__ == 'get_openconfig_system_system_cpus': + keypath = [] + elif func.__name__ == 'get_openconfig_system_system_processes': + keypath = [] + elif func.__name__ == 'get_openconfig_system_components': + keypath = [] + + else: + body = {} + + return keypath,body + + +def run(func, args): + c = openconfig_system_client.Configuration() + c.verify_ssl = False + aa = openconfig_system_client.OpenconfigSystemApi(api_client=openconfig_system_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + + else : + api_response = getattr(aa,func.__name__)(*keypath) + if api_response is None: + print ("Success") + else: + response = api_response.to_dict() + if 'openconfig_systemstate' in response.keys(): + value = response['openconfig_systemstate'] + if value is None: + return + show_cli_output(sys.argv[2], system_state_key_change(value)) + + elif 'openconfig_systemmemory' in response.keys(): + value = response['openconfig_systemmemory'] + if value is None: + return + show_cli_output(sys.argv[2], memory_key_change(value['state'])) + elif 'openconfig_systemcpus' in response.keys(): + value = response['openconfig_systemcpus'] + if value is None: + return + show_cli_output(sys.argv[2], value['cpu']) + elif 'openconfig_systemprocesses' in response.keys(): + value = response['openconfig_systemprocesses'] + if 'pid' not in sys.argv: + if value is None: + return + show_cli_output(sys.argv[2],value['process']) + else: + for proc in value['process']: + if proc['pid'] == int(sys.argv[3]): + show_cli_output(sys.argv[2],util_capitalize(proc['state'])) + return + else: + print("Failed") + except ApiException as e: + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Application Failure" + return + print "%Error: Application Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + #pdb.set_trace() + func = eval(sys.argv[1], globals(), openconfig_system_client.OpenconfigSystemApi.__dict__) + run(func, sys.argv[2:]) + diff --git a/src/CLI/actioner/sonic-cli-udld.py b/src/CLI/actioner/sonic-cli-udld.py new file mode 100755 index 0000000000..ea736da487 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-udld.py @@ -0,0 +1,278 @@ +#!/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 json +import collections +import re +import cli_client as cc +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output + + +def invoke(func, args): + body = None + aa = cc.ApiClient() + + # show udld global + if func == 'get_sonic_udld_sonic_udld_udld_udld_list': + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD/UDLD_LIST={id}', id='GLOBAL') + resp = aa.get(keypath) + if not resp.ok() and resp.error_message() is not None: + if resp.error_message().find('Entry not found') >= 0: + return aa._make_error_response('UDLD not configured') + return resp + + # show udld neighbors + if func == 'get_list_sonic_udld_sonic_udld_udld_port_neigh_table_udld_port_neigh_table_list_zz': + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT_NEIGH_TABLE/UDLD_PORT_NEIGH_TABLE_LIST') + return aa.get(keypath) + + # show udld interface + if func == 'get_sonic_udld_sonic_udld_udld_port_neigh_table_udld_port_neigh_table_list': + return generateShowUdldInterfaceResponse(aa, args) + + # show udld statistics + if func == 'get_list_sonic_udld_sonic_udld_udld_port_table_udld_port_table_list_zz': + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT_TABLE/UDLD_PORT_TABLE_LIST') + return aa.get(keypath) + + # show udld statistics interface + if func == 'get_sonic_udld_sonic_udld_udld_port_table_udld_port_table_list_zz': + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT_TABLE/UDLD_PORT_TABLE_LIST={ifname}', ifname=args[1]) + return aa.get(keypath) + + # Enable UDLD global + if func == 'post_list_sonic_udld_sonic_udld_udld_udld_list' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD') + body=collections.defaultdict(dict) + body["UDLD_LIST"] = [{ + "id": "GLOBAL", + "msg_time": 1, + "multiplier":3, + "admin_enable":True, + "aggressive":False + }] + return aa.post(keypath, body) + + # Disable UDLD global + if func == 'delete_sonic_udld_sonic_udld_udld' : + # Delete all port level udld configs + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT') + resp = aa.delete(keypath) + if not resp.ok(): + return resp + + # Delete global level udld configs + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD') + return aa.delete(keypath) + + # Configure UDLD aggressive + if func == 'patch_sonic_udld_sonic_udld_udld_udld_list_aggressive' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD/UDLD_LIST={id}/aggressive', id='GLOBAL') + body = { "sonic-udld:aggressive": str2bool(args[0]) } + return aa.patch(keypath, body) + + # Configure UDLD message-time + if func == 'patch_sonic_udld_sonic_udld_udld_udld_list_msg_time' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD/UDLD_LIST={id}/msg_time', id='GLOBAL') + if args[0] == '0': + body = { "sonic-udld:msg_time": 1 } + else: + body = { "sonic-udld:msg_time": int(args[0]) } + return aa.patch(keypath, body) + + # Configure UDLD multiplier + if func == 'patch_sonic_udld_sonic_udld_udld_udld_list_multiplier' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD/UDLD_LIST={id}/multiplier', id='GLOBAL') + if args[0] == '0': + body = { "sonic-udld:multiplier": 3 } + else: + body = { "sonic-udld:multiplier": int(args[0]) } + return aa.patch(keypath, body) + + # Enable UDLD at Interface + if func == 'post_list_sonic_udld_sonic_udld_udld_port_udld_port_list' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT') + body=collections.defaultdict(dict) + body["UDLD_PORT_LIST"] = [{ + "ifname": args[0], + "admin_enable":True, + "aggressive":False + }] + return aa.post(keypath, body) + + # Disable UDLD at Interface + if func == 'delete_sonic_udld_sonic_udld_udld_port_udld_port_list' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT/UDLD_PORT_LIST={ifname}', ifname=args[0]) + return aa.delete(keypath) + + # Configure UDLD aggressive at Interface + if func == 'patch_sonic_udld_sonic_udld_udld_port_udld_port_list_aggressive' : + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT/UDLD_PORT_LIST={ifname}/aggressive', ifname=args[1]) + body = { "sonic-udld:aggressive": str2bool(args[0]) } + return aa.patch(keypath, body) + + # enable/disable debug udld at global level + if func == 'udldGlobalDebugHandler' : + return udldGlobalDebugHandler(args) + + # enable/disable debug udld at interface level + if func == 'udldInterfaceDebugHandler' : + return udldInterfaceDebugHandler(args) + + # clear udld statistics + if func == 'udldInterfaceCountersClearHandler' : + return udldInterfaceCountersClearHandler(args) + +def generateShowUdldInterfaceResponse(clientApi, args): + resp_status = 0 + # Retrieve global UDLD message time + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD/UDLD_LIST={id}/msg_time', id='GLOBAL') + resp = clientApi.get(keypath) + gbl_msg_time = 0 + if resp.ok() and 'sonic-udld:msg_time' in resp.content.keys(): + gbl_msg_time = resp.content['sonic-udld:msg_time'] + + # Retrieve port level UDLD configs + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT/UDLD_PORT_LIST={ifname}', ifname=args[1]) + resp = clientApi.get(keypath) + port_conf_dict = {} + if resp.ok() and 'sonic-udld:UDLD_PORT_LIST' in resp.content.keys(): + port_conf_dict = resp.content['sonic-udld:UDLD_PORT_LIST'][0] + resp_status = resp.response.status_code + else: + if resp.error_message().find('Entry not found') >= 0: + return clientApi._make_error_response('UDLD not configured on ' + args[1]) + else: + return resp + + # Retrieve UDLD Global operatioal info + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_GLOBAL_TABLE/UDLD_GLOBAL_TABLE_LIST={id}', id='GLOBAL') + resp = clientApi.get(keypath) + gbl_oper_dict = {} + if resp.ok() and 'sonic-udld:UDLD_GLOBAL_TABLE_LIST' in resp.content.keys(): + gbl_oper_dict = resp.content['sonic-udld:UDLD_GLOBAL_TABLE_LIST'][0] + + # Retrieve UDLD port local status + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT_TABLE/UDLD_PORT_TABLE_LIST={ifname}/status', ifname=args[1]) + resp = clientApi.get(keypath) + port_status = 0 + if resp.ok() and 'sonic-udld:status' in resp.content.keys(): + port_status = resp.content['sonic-udld:status'] + + # Retrieve neighbors info for a given interface + keypath = cc.Path('/restconf/data/sonic-udld:sonic-udld/UDLD_PORT_NEIGH_TABLE/UDLD_PORT_NEIGH_TABLE_LIST={ifname},{index}', ifname=args[1], index='*') + resp = clientApi.get(keypath) + neigh_dict = {} + if resp.ok() and 'sonic-udld:UDLD_PORT_NEIGH_TABLE_LIST' in resp.content.keys(): + neigh_dict = resp.content['sonic-udld:UDLD_PORT_NEIGH_TABLE_LIST'][0] + else: + if resp.error_message().find('Entry not found') < 0: + return resp + + final_dict = {} + final_dict['interface'] = args[1] + final_dict['msg_time'] = gbl_msg_time + final_dict['status'] = port_status + final_dict['port_config'] = port_conf_dict + final_dict['global_oper'] = gbl_oper_dict + final_dict['neighbor'] = neigh_dict + + body=collections.defaultdict(dict) + body['intf_show'] = final_dict + + import requests + presp = requests.Response() + presp.status_code = resp_status + response = cc.Response(presp) + response.content = body + + return response + + +def udldGlobalDebugHandler(args): + if args[0] == '1': + print("Enabled Debug at global level") + else: + print("Disable Debug at global level") + + +def udldInterfaceDebugHandler(args): + if args[0] == '1': + print("Enabled Debug at interface level for " + args[1]) + else: + print("Disable Debug at interface level for " + args[1]) + + +def udldInterfaceCountersClearHandler(args): + if len(args) == 0: + print("Clearing counters for all interfaces") + else: + print("Clearing counters for interface: " + args[0]) + + +def str2bool(s): + return s.lower() in ("yes", "true", "t", "1") + + +def run(func, args): + api_response = invoke(func, args) + # Temporary for Mock CLI. Needs to be removed + if api_response is None: + value = [{'id': 'GLOBAL'}] + show_cli_output(args[0], value) + return + if api_response.ok(): + response = api_response.content + if response is None: + print "Success" + elif 'sonic-udld:UDLD_LIST' in response.keys(): + value = response['sonic-udld:UDLD_LIST'] + if value is None: + return + else: + show_cli_output(args[0], value) + elif 'sonic-udld:UDLD_PORT_TABLE_LIST' in response.keys(): + value = response['sonic-udld:UDLD_PORT_TABLE_LIST'] + if value is None: + return + else: + show_cli_output(args[0], value) + elif 'intf_show' in response.keys(): + value = response['intf_show'] + if value is None: + return + else: + show_cli_output(args[0], value) + elif 'sonic-udld:UDLD_PORT_NEIGH_TABLE_LIST' in response.keys(): + value = response['sonic-udld:UDLD_PORT_NEIGH_TABLE_LIST'] + if value is None: + return + else: + show_cli_output(args[0], value) + else: + print api_response.error_message() + + +if __name__ == '__main__': + pipestr().write(sys.argv) + #pdb.set_trace() + run(sys.argv[1], sys.argv[2:]) + diff --git a/src/CLI/actioner/sonic-cli-vlan.py b/src/CLI/actioner/sonic-cli-vlan.py new file mode 100644 index 0000000000..b8c13aa972 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-vlan.py @@ -0,0 +1,153 @@ +#!/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 +import ast +import collections +import cli_client as cc +from rpipe_utils import pipestr +from scripts.render_cli import show_cli_output + +vlanDict = {} + +class ifInfo: + ifModeDict = {} + oper_status = "down" + + def __init__(self, ifModeDict): + self.ifModeDict = ifModeDict + + def asdict(self): + return {'vlanMembers':self.ifModeDict, 'oper_status':self.oper_status} + +def invoke_api(func, args=[]): + api = cc.ApiClient() + + if func == 'get_sonic_vlan_sonic_vlan': + path = cc.Path('/restconf/data/sonic-vlan:sonic-vlan') + return api.get(path) + + return api.cli_not_implemented(func) + +def getVlanId(key): + try: + return int(key) + except ValueError: + return key + +def updateVlanToIntfMap(vlanTuple, vlanId): + for dict in vlanTuple: + if "ifname" in dict: + ifName = dict["ifname"] + if "name" in dict: + vlanName = dict["name"] + if vlanId: + if vlanName != vlanId: + continue + vlanName = vlanName[len("Vlan"):] + + if "tagging_mode" in dict: + ifMode = dict["tagging_mode"] + + if not vlanName: + return + + if vlanDict.get(vlanName) == None: + ifModeDict = {} + + if ifMode and ifName: + ifModeDict[ifName] = ifMode + ifData = ifInfo(ifModeDict) + vlanDict[vlanName] = ifData + else: + ifData = vlanDict.get(vlanName) + existingifDict = ifData.ifModeDict + if ifMode and ifName: + existingifDict[ifName] = ifMode + +def updateVlanInfoMap(vlanTuple, vlanId): + for dict in vlanTuple: + if "name" in dict: + vlanName = dict["name"] + if vlanId: + if vlanName != vlanId: + continue + vlanName = vlanName[len("Vlan"):] + if not vlanName: + return + + operStatus = "down" + if "oper_status" in dict: + operStatus = dict["oper_status"] + + if vlanDict.get(vlanName) == None: + ifModeDict = {} + + ifData = ifInfo(ifModeDict) + ifData.oper_status = operStatus + vlanDict[vlanName] = ifData + else: + ifData = vlanDict.get(vlanName) + ifData.oper_status = operStatus + +def run(func, args): + response = invoke_api(func, args) + + if response.ok(): + if response.content is not None: + # Get Command Output + api_response = response.content + if 'sonic-vlan:sonic-vlan' in api_response: + value = api_response['sonic-vlan:sonic-vlan'] + if 'VLAN_MEMBER_TABLE' in value: + vlanMemberCont = value['VLAN_MEMBER_TABLE'] + if 'VLAN_MEMBER_TABLE_LIST' in vlanMemberCont: + vlanMemberTup = vlanMemberCont['VLAN_MEMBER_TABLE_LIST'] + updateVlanToIntfMap(vlanMemberTup, args[0]) + if 'VLAN_TABLE' in value: + vlanCont = value['VLAN_TABLE'] + if 'VLAN_TABLE_LIST' in vlanCont: + vlanTup = vlanCont['VLAN_TABLE_LIST'] + updateVlanInfoMap(vlanTup, args[0]) + if api_response is None: + print("Failed") + else: + vDict = {} + for key, val in vlanDict.iteritems(): + vDict[key] = vlanDict[key].asdict() + for key, val in vDict.iteritems(): + sortMembers = collections.OrderedDict(sorted(val['vlanMembers'].items(), key=lambda t: t[1])) + val['vlanMembers'] = sortMembers + vDictSorted = collections.OrderedDict(sorted(vDict.items(), key = lambda t: getVlanId(t[0]))) + if func == 'get_sonic_vlan_sonic_vlan': + show_cli_output(args[1], vDictSorted) + else: + return + + else: + print response.error_message() + +if __name__ == '__main__': + + pipestr().write(sys.argv) + func = sys.argv[1] + + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-vrf.py b/src/CLI/actioner/sonic-cli-vrf.py new file mode 100644 index 0000000000..ff22860caa --- /dev/null +++ b/src/CLI/actioner/sonic-cli-vrf.py @@ -0,0 +1,140 @@ +#!/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 +import ast +import openconfig_network_instance_client +from openconfig_network_instance_client.rest import ApiException +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 generate_body(func, args): + body = None + # Get the all vrfs. + if func.__name__ == 'get_list_openconfig_network_instance_network_instances_network_instance': + keypath = [] + + # Get a specific vrf. + elif func.__name__ == 'get_openconfig_network_instance_network_instances_network_instance': + keypath = [ args[0] ] + + # Configure management vrf. maybe just set mgmt here without args[0] + elif func.__name__ == 'patch_openconfig_network_instance_network_instances_network_instance' : + keypath = [ args[0] args[1] args[2] ] + body = { "openconfig-network-instance:config": { + "name": args[0], + "type": args[1], + "enabled": args[2] + "description": "" + } + } + + # Delete management vrf. + elif func.__name__ == 'delete_openconfig_network_instance_network_instances_network_instance': + keypath = [ args[0] args[1] args[2] ] + body = { "openconfig-network-instance:config": { + "name": args[0], + "type": args[1], + "enabled": args[2] + "description": "" + } + } + + else: + body = {} + + if body is not None: + body = json.dumps(body,ensure_ascii=False, indent=4, separators=(',', ':')) + return keypath, ast.literal_eval(body) + else: + return keypath, body + + +def run(func, args): + + c = openconfig_network_instance_client.Configuration() + c.verify_ssl = False + aa = openconfig_network_instance_client.OpenconfigNetworkInstanceApi(api_client=openconfig_network_instance_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + else : + api_response = getattr(aa,func.__name__)(*keypath) + + if api_response is None: + print ("Success") + else: + response = api_response.to_dict() + if 'openconfig_network_instancenetwork_instance' in response.keys(): + value = response['openconfig_network_instancenetwork_instance'] + if value is None: + print("Success") + else: + print ("Failed") + else: + print("Failed") + + except ApiException as e: + print("Exception when calling OpenconfigNetworkInstanceApi->%s : %s\n" %(func.__name__, e)) + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Transaction Failure" + return + print "%Error: Transaction Failure" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + #pdb.set_trace() + func = eval(sys.argv[1], globals(), openconfig_network_instance_client.OpenconfigNetworkInstanceApi.__dict__) + run(func, sys.argv[2:]) + diff --git a/src/CLI/actioner/sonic-cli-vxlan.py b/src/CLI/actioner/sonic-cli-vxlan.py new file mode 100755 index 0000000000..2a3703b5bd --- /dev/null +++ b/src/CLI/actioner/sonic-cli-vxlan.py @@ -0,0 +1,155 @@ +#!/usr/bin/python +import sys +import time +import json +import ast +import sonic_vxlan_client +from rpipe_utils import pipestr +from sonic_vxlan_client.rest import ApiException +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 generate_body(func, args): + body = None + # Get the rules of all ACL table entries. + if func.__name__ == 'patch_list_sonic_vxlan_sonic_vxlan_vxlan_tunnel_vxlan_tunnel_list': + keypath = [] + body = { + "sonic-vxlan:VXLAN_TUNNEL_LIST": [ + { + "name": args[0][5:], + "src_ip": args[1] + } + ] + } + elif func.__name__ == 'delete_sonic_vxlan_sonic_vxlan_vxlan_tunnel': + #keypath = [ args[0][5:] ] + keypath = [] + elif func.__name__ == 'patch_list_sonic_vxlan_sonic_vxlan_suppress_vlan_neigh_suppress_vlan_neigh_list': + keypath = [] + body = { + "sonic-vxlan:SUPPRESS_VLAN_NEIGH_LIST": [ + { + "name": args[0], + "suppress": 'on' + } + ] + } + elif func.__name__ == 'delete_sonic_vxlan_sonic_vxlan_suppress_vlan_neigh_suppress_vlan_neigh_list': + keypath = [ args[0] ] + elif func.__name__ == 'patch_list_sonic_vxlan_sonic_vxlan_evpn_nvo_evpn_nvo_list': + keypath = [] + body = { + "sonic-vxlan:EVPN_NVO_LIST": [ + { + "name": args[0][4:], + "source_vtep": args[1] + } + ] + } + elif func.__name__ == 'delete_sonic_vxlan_sonic_vxlan_evpn_nvo': + #keypath = [ args[0][4:] ] + keypath = [] + elif func.__name__ == 'patch_list_sonic_vxlan_sonic_vxlan_vxlan_tunnel_map_vxlan_tunnel_map_list': + keypath = [] + body = { + "sonic-vxlan:VXLAN_TUNNEL_MAP_LIST": [ + { + "name": args[0][5:], + "mapname": 'map_'+ args[1] + '_' + args[2], + "vlan": 'Vlan' + args[2], + "vni": int(args[1]) + } + ] + } + elif func.__name__ == 'delete_sonic_vxlan_sonic_vxlan_vxlan_tunnel_map_vxlan_tunnel_map_list': + keypath = [ args[0][5:] , 'map_'+ args[1] + '_' + args[2]] + else: + body = {} + + return keypath,body + +def getId(item): + prfx = "Ethernet" + state_dict = item['state'] + ifName = state_dict['name'] + + if ifName.startswith(prfx): + ifId = int(ifName[len(prfx):]) + return ifId + return ifName + +def run(func, args): + + c = sonic_vxlan_client.Configuration() + c.verify_ssl = False + aa = sonic_vxlan_client.SonicVxlanApi(api_client=sonic_vxlan_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + else : + api_response = getattr(aa,func.__name__)(*keypath) + + if api_response is None: + print ("Success") + else: + # Get Command Output + api_response = aa.api_client.sanitize_for_serialization(api_response) +# if 'openconfig-interfaces:interfaces' in api_response: +# value = api_response['openconfig-interfaces:interfaces'] +# if 'interface' in value: +# tup = value['interface'] +# value['interface'] = sorted(tup, key=getId) + + if api_response is None: + print("Failed") + else: + return + + except ApiException as e: + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Transaction Failure" + return + print "%Error: Transaction Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + func = eval(sys.argv[1], globals(), sonic_vxlan_client.SonicVxlanApi.__dict__) + + run(func, sys.argv[2:]) diff --git a/src/CLI/actioner/sonic-cli-ztp.py b/src/CLI/actioner/sonic-cli-ztp.py new file mode 100644 index 0000000000..4df688dbe4 --- /dev/null +++ b/src/CLI/actioner/sonic-cli-ztp.py @@ -0,0 +1,113 @@ +#!/usr/bin/python +import sys +import time +import json +import ast +import openconfig_platform_client +from rpipe_utils import pipestr +from openconfig_platform_client.rest import ApiException +from scripts.render_cli import show_cli_output + + +import urllib3 +urllib3.disable_warnings() + +plugins = dict() +temp_resp = { 'ZTP Admin Mode' : 'True', + 'ZTP Service' : 'Inactive', + 'ZTP Status' : 'SUCCESS', + 'ZTP Source' : 'dhcp-opt67 (eth0)', + 'Runtime' : '05m 31s', + 'Timestamp' : '2019-09-11 19:12:16 UTC', + 'ZTP JSON Version' : '1.0', + 'activity-string':'ZTP Service is not running', + 'config-list' : {'configdb-json':{ + 'Status' : 'SUCCESS', + 'Runtime' : '02m 48s', + 'Timestamp' : '2019-09-11 19:11:55 UTC', + 'Exit Code' : '0', + 'Ignore Result' : 'False' }, + 'connectivity-check':{ + 'Status' : 'SUCCESS', + 'Runtime' : '04s', + 'Timestamp' : '2019-09-11 19:12:16 UTC', + 'Exit Code' : '0', + 'Ignore Result' : 'False'} + } + } +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 generate_body(func, args): + body = None + # Get the rules of all ACL table entries. + + if func.__name__ == 'get_openconfig_platform_components': + keypath = [] + + else: + body = {} + + return keypath,body + + +def run(func, args): + c = openconfig_platform_client.Configuration() + c.verify_ssl = False + aa = openconfig_platform_client.OpenconfigPlatformApi(api_client=openconfig_platform_client.ApiClient(configuration=c)) + + # create a body block + keypath, body = generate_body(func, args) + + try: + if body is not None: + api_response = getattr(aa,func.__name__)(*keypath, body=body) + + else : + api_response = getattr(aa,func.__name__)(*keypath) + + if api_response is None: + print ("Success") + else: + #api_response = aa.api_client.sanitize_for_serialization(api_response) + if 'ztp-status' in sys.argv: + show_cli_output(sys.argv[2],temp_resp) + else: + print('Success') + + except ApiException as e: + if e.body != "": + body = json.loads(e.body) + if "ietf-restconf:errors" in body: + err = body["ietf-restconf:errors"] + if "error" in err: + errList = err["error"] + + errDict = {} + for dict in errList: + for k, v in dict.iteritems(): + errDict[k] = v + + if "error-message" in errDict: + print "%Error: " + errDict["error-message"] + return + print "%Error: Transaction Failure" + return + print "%Error: Transaction Failure" + else: + print "Failed" + +if __name__ == '__main__': + + pipestr().write(sys.argv) + #pdb.set_trace() + func = eval(sys.argv[1], globals(), openconfig_platform_client.OpenconfigPlatformApi.__dict__) + run(func, sys.argv[2:]) + diff --git a/src/CLI/actioner/tam.py b/src/CLI/actioner/tam.py new file mode 100755 index 0000000000..3bc611bc1b --- /dev/null +++ b/src/CLI/actioner/tam.py @@ -0,0 +1,172 @@ +#!/usr/bin/python + +############################################################################ +# +# tam is a tool for handling TAM commands. +# +############################################################################ + +import argparse +import getopt +import json +import os +import re +import sys +import swsssdk +from scripts.render_cli import show_cli_output +from swsssdk import ConfigDBConnector +from os import path + +TAM_DEVICE_TABLE_PREFIX = "TAM_DEVICE_TABLE" +TAM_COLLECTOR_TABLE_PREFIX = "TAM_COLLECTOR_TABLE" + +collectorheader = ['NAME', 'IP TYPE', 'IP', 'PORT'] + +class Tam(object): + + def __init__(self): + # connect CONFIG DB + self.config_db = ConfigDBConnector() + self.config_db.connect() + + # connect APPL DB + self.app_db = ConfigDBConnector() + self.app_db.db_connect('APPL_DB') + + def get_tam_collector_info(self, k): + api_response = {} + key = TAM_COLLECTOR_TABLE_PREFIX + '|' + k + raw_coll_data = self.config_db.get_all(self.config_db.CONFIG_DB, key) + api_response['coll-key'] = k + api_response['each-coll-data'] = raw_coll_data + return api_response , raw_coll_data + + def get_print_all_tam_collectors(self, name): + coll_dict = {} + coll_list = [] + if name != 'all': + api_response, entryfound = self.get_tam_collector_info(name) + if entryfound is not None: + coll_list.append(api_response) + else: + table_data = self.config_db.get_keys(TAM_COLLECTOR_TABLE_PREFIX) + # Get data for all keys + for k in table_data: + api_each_flow_response, entryfound = self.get_tam_collector_info(k) + if entryfound is not None: + coll_list.append(api_each_flow_response) + + coll_dict['flow-list'] = coll_list + show_cli_output("show_collector.j2", coll_dict) + return + + def config_device_id(self, args): + key = 'device' + entry = self.config_db.get_entry(TAM_DEVICE_TABLE_PREFIX, key) + if entry is None: + if args.deviceid: + self.config_db.set_entry(TAM_DEVICE_TABLE_PREFIX, key, {'deviceid' : args.deviceid}) + else: + if args.deviceid: + entry_value = entry.get('deviceid', []) + + if entry_value != args.deviceid: + self.config_db.mod_entry(TAM_DEVICE_TABLE_PREFIX, key, {'deviceid' : args.deviceid}) + return + + def config_collector(self, args): + if args.iptype == 'ipv4': + if args.ipaddr == "0.0.0.0": + print "Collector IP should be non-zero ip address" + return False + + if args.iptype == 'ipv6': + print "IPv6 Collector type not supported" + return False + + self.config_db.mod_entry(TAM_COLLECTOR_TABLE_PREFIX, args.collectorname, {'ipaddress-type' : args.iptype, 'ipaddress' : args.ipaddr, 'port' : args.port}) + + return + + def clear_device_id(self): + key = 'device' + entry = self.config_db.get_entry(TAM_DEVICE_TABLE_PREFIX, key) + if entry: + self.config_db.set_entry(TAM_DEVICE_TABLE_PREFIX, key, None) + return + + def clear_collector(self, args): + key = args.collectorname + entry = self.config_db.get_entry(TAM_COLLECTOR_TABLE_PREFIX, key) + if entry: + self.config_db.set_entry(TAM_COLLECTOR_TABLE_PREFIX, key, None) + else: + print "Entry Not Found" + return False + return + + def show_device_id(self): + key = TAM_DEVICE_TABLE_PREFIX + '|' + 'device' + data = self.config_db.get_all(self.config_db.CONFIG_DB, key) + print "TAM Device identifier" + print "-------------------------------" + if data: + if 'deviceid' in data: + print "Device Identifier - ", data['deviceid'] + return + + def show_collector(self, args): + self.get_print_all_tam_collectors(args.collectorname) + return + +def main(): + + parser = argparse.ArgumentParser(description='Handles TAM commands', + version='1.0.0', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""" +Examples: + tam -config -deviceid value + tam -config -collector collectorname -iptype ipv4/ipv6 -ip ipaddr -port value + tam -clear -device_id + tam -clear -collector collectorname + tam -show -device_id + tam -show -collector collectorname +""") + + parser.add_argument('-clear', '--clear', action='store_true', help='Clear tam information') + parser.add_argument('-show', '--show', action='store_true', help='Show tam information') + parser.add_argument('-config', '--config', action='store_true', help='Config tam information') + parser.add_argument('-device', '--device', action='store_true', help='tam device identifier') + parser.add_argument('-deviceid', '--deviceid', type=int, help='tam device identifier') + parser.add_argument('-collector', '--collectorname', type=str, help='tam collector name') + parser.add_argument('-iptype', '--iptype', type=str, choices=['ipv4', 'ipv6'], help='tam collector IP type') + parser.add_argument('-ipaddr', '--ipaddr', type=str, help='tam collector ip') + parser.add_argument('-port', '--port', type=str, help='tam collector port') + parser.add_argument('-templ', '--template', action='store_true', help='ifa template to be used') + parser.add_argument('-showcollector.j2', '--showcollector', action='store_true', help='ifa template for collector to be used') + + args = parser.parse_args() + + tam = Tam() + + if args.config: + if args.device: + tam.config_device_id(args) + elif args.collectorname and args.iptype and args.ipaddr and args.port: + tam.config_collector(args) + elif args.clear: + if args.device: + tam.clear_device_id() + elif args.collectorname: + tam.clear_collector(args) + elif args.show: + if args.device: + tam.show_device_id() + elif args.collectorname: + tam.show_collector(args) + + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/src/CLI/actioner/ts.py b/src/CLI/actioner/ts.py new file mode 100755 index 0000000000..bb9e593b6b --- /dev/null +++ b/src/CLI/actioner/ts.py @@ -0,0 +1,283 @@ +#!/usr/bin/python + +############################################################################ +# +# ts is a tool for handling TAM INT IFA TS commands. +# +############################################################################ + +import argparse +import getopt +import json +import os +import re +import sys +import swsssdk +from swsssdk import ConfigDBConnector +from os import path +from scripts.render_cli import show_cli_output + +TAM_INT_IFA_FLOW_TS_TABLE_PREFIX = "TAM_INT_IFA_TS_FLOW" +TAM_INT_IFA_TS_FEATURE_TABLE_PREFIX = "TAM_INT_IFA_TS_FEATURE_TABLE" +TAM_DEVICE_TABLE_PREFIX = "TAM_DEVICE_TABLE" +TAM_COLLECTOR_TABLE_PREFIX = "TAM_COLLECTOR_TABLE" +ACL_RULE_TABLE_PREFIX = "ACL_RULE" +ACL_TABLE_PREFIX = "ACL_TABLE" + +class Ts(object): + + def __init__(self): + # connect CONFIG DB + self.config_db = ConfigDBConnector() + self.config_db.connect() + + # connect COUNTER DB + self.counters_db = ConfigDBConnector() + self.counters_db.db_connect('COUNTERS_DB') + + # connect APPL DB + self.app_db = ConfigDBConnector() + self.app_db.db_connect('APPL_DB') + + def config_enable(self, args): + """ Enable ifa """ + key = 'feature' + self.config_db.set_entry(TAM_INT_IFA_TS_FEATURE_TABLE_PREFIX, key, {'enable' :"true"}) + print "Enabled IFA" + + return + + def config_disable(self, args): + """ Disable ifa """ + key = 'feature' + self.config_db.set_entry(TAM_INT_IFA_TS_FEATURE_TABLE_PREFIX, key, {'enable' :"false"}) + print "Disabled IFA" + + return + + def config_flow(self, args): + key = TAM_INT_IFA_FLOW_TS_TABLE_PREFIX + '|' + args.flowname + entry = self.config_db.get_all(self.config_db.CONFIG_DB, key) + if entry is None: + if args.acl_table_name: + self.config_db.mod_entry(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX, args.flowname, {'acl-table-name' : args.acl_table_name}) + if args.acl_rule_name: + self.config_db.mod_entry(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX, args.flowname, {'acl-rule-name' : args.acl_rule_name}) + else: + print "Entry Already Exists" + return False + return + + def clear_each_flow(self, flowname): + entry = self.config_db.get_entry(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX, flowname) + if entry: + self.config_db.set_entry(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX, flowname, None) + else: + print "Entry Not Found" + return False + + return + + def clear_flow(self, args): + key = args.flowname + if key == "all": + # Get all the flow keys + table_data = self.config_db.get_keys(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX) + if not table_data: + return True + # Clear each flow key + for key in table_data: + self.clear_each_flow(key) + else: + # Clear the specified flow entry + self.clear_each_flow(key) + + return + + def show_flow(self, args): + self.get_print_all_ifa_flows(args.flowname) + return + + def show_status(self): + # Get data for all keys + flowtable_keys = self.config_db.get_keys(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX) + + api_response = {} + key = TAM_INT_IFA_TS_FEATURE_TABLE_PREFIX + '|' + 'feature' + raw_data_feature = self.config_db.get_all(self.config_db.CONFIG_DB, key) + api_response['ietf-ts:feature-data'] = raw_data_feature + api_inner_response = {} + api_inner_response["num-of-flows"] = len(flowtable_keys) + api_response['ietf-ts:num-of-flows'] = api_inner_response + key = TAM_DEVICE_TABLE_PREFIX + '|' + 'device' + raw_data_device = self.config_db.get_all(self.config_db.CONFIG_DB, key) + api_response['ietf-ts:device-data'] = raw_data_device + show_cli_output("show_status.j2", api_response) + + return + + def get_ifa_flow_stat(self, flowname): + api_response_stat = {} + api_response, entryfound = self.get_ifa_flow_info(flowname) + api_response_stat['flow-name'] = flowname + if entryfound is not None: + for k in api_response: + if k == "ietf-ts:each-flow-data": + acl_rule_name = api_response['ietf-ts:each-flow-data']['acl-rule-name'] + acl_table_name = api_response['ietf-ts:each-flow-data']['acl-table-name'] + api_response_stat['rule-name'] = acl_rule_name + api_response_stat['table-name'] = acl_table_name + + acl_rule_keys = self.config_db.get_keys(ACL_RULE_TABLE_PREFIX) + for acl_rule_key in acl_rule_keys: + if acl_rule_key[1] == acl_rule_name: + acl_counter_key = 'COUNTERS:' + acl_rule_key[0] + ':' + acl_rule_key[1] + raw_ifa_stats = self.counters_db.get_all(self.counters_db.COUNTERS_DB, acl_counter_key) + api_response_stat['ietf-ts:ifa-stats'] = raw_ifa_stats + + return api_response_stat, entryfound + + def get_print_all_ifa_stats(self, name): + stat_dict = {} + stat_list = [] + if name != 'all': + api_response, entryfound = self.get_ifa_flow_stat(name) + if entryfound is not None: + stat_list.append(api_response) + else: + table_data = self.config_db.get_keys(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX) + # Get data for all keys + for k in table_data: + api_each_stat_response, entryfound = self.get_ifa_flow_stat(k) + if entryfound is not None: + stat_list.append(api_each_stat_response) + + stat_dict['stat-list'] = stat_list + show_cli_output("show_statistics_flow.j2", stat_dict) + return + + def show_statistics(self, args): + self.get_print_all_ifa_stats(args.flowname) + return + + + def get_ifa_flow_info(self, k): + flow_data = {} + flow_data['acl-table-name'] = '' + flow_data['sampling-rate'] = '' + flow_data['collector'] = '' + + api_response = {} + key = TAM_INT_IFA_FLOW_TS_TABLE_PREFIX + '|' + k + raw_flow_data = self.config_db.get_all(self.config_db.CONFIG_DB, key) + api_response['ietf-ts:flow-key'] = k + api_response['ietf-ts:each-flow-data'] = raw_flow_data + return api_response , raw_flow_data + + def get_print_all_ifa_flows(self, name): + flow_dict = {} + flow_list = [] + if name != 'all': + api_response, entryfound = self.get_ifa_flow_info(name) + if entryfound is not None: + flow_list.append(api_response) + else: + table_data = self.config_db.get_keys(TAM_INT_IFA_FLOW_TS_TABLE_PREFIX) + # Get data for all keys + for k in table_data: + api_each_flow_response, entryfound = self.get_ifa_flow_info(k) + if entryfound is not None: + flow_list.append(api_each_flow_response) + + flow_dict['flow-list'] = flow_list + show_cli_output("show_flow.j2", flow_dict) + return + + def get_ifa_supported_info(self): + key = 'TAM_INT_IFA_TS_FEATURE_TABLE|feature' + data = self.config_db.get_all(self.config_db.CONFIG_DB, key) + + if data is None: + return + + if data['enable'] == "true" : + print "TAM INT IFA TS Supported - True" + return True + elif data['enable'] == "false" : + print "TAM INT IFA TS Supported - False " + return False + + return + + def get_ifa_enabled_info(self): + print "In get_ifa_enabled_info" + key = 'SWITCH_TABLE:switch' + data = self.app_db.get(self.app_db.APPL_DB, key, 'ifa_enabled') + + if data and data == 'True': + return True + + return True + + +def main(): + + parser = argparse.ArgumentParser(description='Handles MoD commands', + version='1.0.0', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""" +Examples: + ts -config -flow flowname -acl_table acl_table_name -acl_rule acl_rule_name + ts -config -enable + ts -config -disable + ts -clear -flow flowname +""") + + parser.add_argument('-clear', '--clear', action='store_true', help='Clear tam_int_ifa information') + parser.add_argument('-show', '--show', action='store_true', help='Show tam_int_ifa information') + parser.add_argument('-config', '--config', action='store_true', help='Config tam_int_ifa information') + parser.add_argument('-enable', '-enable', action='store_true', help='Enable Tam Int Ifa') + parser.add_argument('-disable', '-disable', action='store_true', help='Disable Tam Int Ifa') + parser.add_argument('-flow', '--flowname', type=str, help='ifa flow name') + parser.add_argument('-acl_table_name', '--acl_table_name', type=str, help='ifa acl table name') + parser.add_argument('-acl_rule_name', '--acl_rule_name', type=str, help='ifa acl rule name') + parser.add_argument('-status', '--status', action='store_true', help='ifa status') + parser.add_argument('-supported', '--supported', action='store_true', help='Check if tam-int-ifa supported') + parser.add_argument('-statistics', '--statistics', action='store_true', help='ifa statistics') + parser.add_argument('-templ', '--template', action='store_true', help='ifa template to be used') + parser.add_argument('-showsupported.j2', '--showsupported', action='store_true', help='ifa template to be used') + parser.add_argument('-showstatus.j2', '--showstatus', action='store_true', help='ifa status to be used') + parser.add_argument('-showflow.j2', '--showflow', action='store_true', help='ifa flow to be used') + parser.add_argument('-showstatisticsflow.j2', '--showstatistics', action='store_true', help='ifa statistics to be used') + + + args = parser.parse_args() + + ts = Ts() + + + if args.config: + if args.enable: + ts.config_enable(args) + elif args.disable: + ts.config_disable(args) + elif args.flowname: + ts.config_flow(args) + + elif args.clear: + if args.flowname: + ts.clear_flow(args) + elif args.show: + if args.status: + ts.show_status() + elif args.statistics: + ts.show_statistics(args) + elif args.flowname: + ts.show_flow(args) + elif args.supported: + ts.get_ifa_supported_info() + + sys.exit(0) + +if __name__ == "__main__": + main() diff --git a/src/CLI/clicfg/mgmt_clish_entities.xsl b/src/CLI/clicfg/mgmt_clish_entities.xsl new file mode 100644 index 0000000000..a21cca8d99 --- /dev/null +++ b/src/CLI/clicfg/mgmt_clish_entities.xsl @@ -0,0 +1,26 @@ + + + + + + + + +<!ENTITY ""> + + diff --git a/src/CLI/clicfg/mgmt_clish_entities_macro.xsl b/src/CLI/clicfg/mgmt_clish_entities_macro.xsl new file mode 100644 index 0000000000..f20fd62e6f --- /dev/null +++ b/src/CLI/clicfg/mgmt_clish_entities_macro.xsl @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clicfg/mgmt_clish_feature_master.xsd b/src/CLI/clicfg/mgmt_clish_feature_master.xsd new file mode 100644 index 0000000000..fd7c330370 --- /dev/null +++ b/src/CLI/clicfg/mgmt_clish_feature_master.xsd @@ -0,0 +1,98 @@ + + + + + + + This contains the allowed feature names for any platform specific customizable feature names. If you are adding a new platform customizable feature, add a record in the enumerated list here. + + + + + + + + + This contains the allowed feature-value names for any platform specific customizable feature-value strings. If you are adding a new platform customizable entity, add a record in the enumerated list here. + + + + + + + + + + + + This represents a feature-value entity. Do not add or delete anything here. +For adding a new feature-value, use entityname_t. For adding constraints on the count of +entities, use entity_list_t + + + + + + + + + + + + + + + This represents a feature-value entity. Do not add or delete anything here. +For adding a new feature-value, use entityname_t. For adding constraints on the count +of entities, use entity_list_t + + + + + + + + + + + + + + + + + + + + This is the top level module that lists the master list of all features and feature-value names. +If you are adding a new feature, add a record in the featurename_t. +If you are adding a new feature-value, add a record in the entityname_t + + + + + + + + + + + diff --git a/src/CLI/clicfg/mgmt_clish_features.xsl b/src/CLI/clicfg/mgmt_clish_features.xsl new file mode 100644 index 0000000000..2c9ebad834 --- /dev/null +++ b/src/CLI/clicfg/mgmt_clish_features.xsl @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clicfg/mgmt_clish_platform.xml b/src/CLI/clicfg/mgmt_clish_platform.xml new file mode 100644 index 0000000000..f421e8ce26 --- /dev/null +++ b/src/CLI/clicfg/mgmt_clish_platform.xml @@ -0,0 +1,38 @@ + + + + + + + + + START_PORT_ID + MAX_PORT_ID + START_SUB_PORT_ID + MAX_SUB_PORT_ID + MAX_MTU + + diff --git a/src/CLI/clitree/Makefile b/src/CLI/clitree/Makefile new file mode 100644 index 0000000000..0bb140c414 --- /dev/null +++ b/src/CLI/clitree/Makefile @@ -0,0 +1,44 @@ +########################################################################### +# +# 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. +# +########################################################################### + +.PHONY: install + +# TGT_DIR is the directory wher all prepreocssed cli model, actioner and +# renderer scripts and temlates will be dumped. Can be overidden from caller. +TGT_DIR ?= $(SONIC_CLI_ROOT)/target + +all: + mkdir -p ${TGT_DIR}/command-tree ${TGT_DIR}/cli-macro ${TGT_DIR}/render-templates ${TGT_DIR}/scripts + cp -r cli-xml/include ${TGT_DIR}/command-tree + cp cli-xml/*.xml ${TGT_DIR}/command-tree + cp macro/*.xml ${TGT_DIR}/cli-macro + (cd scripts;./klish_platform_features_process.sh ../../clicfg ${TGT_DIR}) + python scripts/klish_preproc_cmdtree.py ${TGT_DIR}/command-tree ${TGT_DIR}/cli-macro 3 + cp ./../actioner/*.py ${TGT_DIR}/. + cp ../renderer/scripts/*.py ${TGT_DIR}/scripts + cp ../renderer/templates/* ${TGT_DIR}/render-templates + cp scripts/sonic-clish.xsd ${TGT_DIR}/command-tree + (cd ${TGT_DIR}/command-tree ; xmllint --noout --schema sonic-clish.xsd ${TGT_DIR}/command-tree/*.xml && \ + xmllint --noout --schema sonic-clish.xsd ${TGT_DIR}/command-tree/include/*.xml) || exit 1 + rm -rf ${TGT_DIR}/cli-macro + rm -rf scripts/*.pyc + +clean: + rm -rf ${TGT_DIR} + @echo "Clitree Cleaned" + diff --git a/src/CLI/clitree/cli-xml/acl.xml b/src/CLI/clitree/cli-xml/acl.xml new file mode 100644 index 0000000000..b78c72f459 --- /dev/null +++ b/src/CLI/clitree/cli-xml/acl.xml @@ -0,0 +1,218 @@ + + + + + + + + + + + + if test "${access-list-name}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-acl.py get_openconfig_acl_acl_acl_sets show_access_list.j2 + else + python $SONIC_CLI_ROOT/sonic-cli-acl.py get_openconfig_acl_acl_acl_sets_acl_set_acl_entries ${access-list-name} ACL_IPV4 show_access_list.j2 + fi + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-acl.py get_openconfig_acl_acl_interfaces show_access_group.j2 + + + + + + + + + + + + + + + if test "${direction-switch}" = "in"; then + python $SONIC_CLI_ROOT/sonic-cli-acl.py patch_list_openconfig_acl_acl_interfaces_interface ${access-list-name} ACL_IPV4 ${iface} ingress + else + python $SONIC_CLI_ROOT/sonic-cli-acl.py patch_list_openconfig_acl_acl_interfaces_interface ${access-list-name} ACL_IPV4 ${iface} egress + fi + + + + + + + + + + if test "${direction-switch}" = "in"; then + python $SONIC_CLI_ROOT/sonic-cli-acl.py delete_openconfig_acl_acl_interfaces_interface_ingress_acl_sets_ingress_acl_set ${iface} ${access-list-name} ACL_IPV4 + else + python $SONIC_CLI_ROOT/sonic-cli-acl.py delete_openconfig_acl_acl_interfaces_interface_egress_acl_sets_egress_acl_set ${iface} ${access-list-name} ACL_IPV4 + fi + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-acl.py patch_openconfig_acl_acl_acl_sets_acl_set ${access-list-name} ACL_IPV4 + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-acl.py delete_openconfig_acl_acl_acl_sets_acl_set ${access-list-name} ACL_IPV4 + + + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-acl.py delete_openconfig_acl_acl_acl_sets_acl_set_acl_entries_acl_entry ${name} ACL_IPV4 ${seq-no} + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-acl.py patch_list_openconfig_acl_acl_acl_sets_acl_set_acl_entries_acl_entry ${name} ACL_IPV4 ${__params} + + + + + diff --git a/src/CLI/clitree/cli-xml/configure_mode.xml b/src/CLI/clitree/cli-xml/configure_mode.xml new file mode 100644 index 0000000000..72c70bac16 --- /dev/null +++ b/src/CLI/clitree/cli-xml/configure_mode.xml @@ -0,0 +1,38 @@ + + + + + + + + + + diff --git a/src/CLI/clitree/cli-xml/drop-monitor.xml b/src/CLI/clitree/cli-xml/drop-monitor.xml new file mode 100755 index 0000000000..bf8c9d74f4 --- /dev/null +++ b/src/CLI/clitree/cli-xml/drop-monitor.xml @@ -0,0 +1,152 @@ + + + + + + +]> + + + + + + + python $SONIC_CLI_ROOT/drop-monitor.py -clear -sample ${sample-name} + + + + + + + + + python $SONIC_CLI_ROOT/drop-monitor.py -config -sample ${sample-name} -rate ${rate-name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${tam-drop-monitor-subcommands}" = "flow"; then + python $SONIC_CLI_ROOT/drop-monitor.py -show -flow ${flow-name} -templ -showflow.j2 + elif test "${tam-drop-monitor-subcommands}" = "statistics"; then + python $SONIC_CLI_ROOT/drop-monitor.py -show -statistics -flow ${flow-name} -templ -showstatisticsflow.j2 + elif test "${tam-drop-monitor-subcommands}" = "aging-interval"; then + python $SONIC_CLI_ROOT/drop-monitor.py -show --aginginterval 1 + fi + + + + + + + + + + + + + python $SONIC_CLI_ROOT/drop-monitor.py -show -sample ${sample-name} -templ -showsample.j2 + + + + + + + + + + + + + + + + + + + + + if test "${drop-options}" = "flow-name"; then + python $SONIC_CLI_ROOT/drop-monitor.py -clear -dropmonitor -flow ${flow-name} + elif test "${drop-options}" = "all"; then + python $SONIC_CLI_ROOT/drop-monitor.py -clear -dropmonitor -flow all + fi + + + + + + python $SONIC_CLI_ROOT/drop-monitor.py -clear -dropmonitor --aginginterval 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/drop-monitor.py -config -dropmonitor -flow ${flow-name} --acl_table ${acl-table-name} --acl_rule ${acl-rule-name} --dropcollector ${collector-name} --dropsample ${sampling-name} + + + + + + + + python $SONIC_CLI_ROOT/drop-monitor.py -config -dropmonitor --aginginterval ${aging-interval-time} + + + + diff --git a/src/CLI/clitree/cli-xml/enable_mode.xml b/src/CLI/clitree/cli-xml/enable_mode.xml new file mode 100644 index 0000000000..df8673660e --- /dev/null +++ b/src/CLI/clitree/cli-xml/enable_mode.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${cmd} + + + + diff --git a/src/CLI/clitree/cli-xml/image.xml b/src/CLI/clitree/cli-xml/image.xml new file mode 100644 index 0000000000..b1a96c9e71 --- /dev/null +++ b/src/CLI/clitree/cli-xml/image.xml @@ -0,0 +1,80 @@ + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-img.py get_openconfig_platform_components show_image.j2 ${__full_line} + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-img.py get_openconfig_platform_components ${img-name} ${__full_line} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-img.py get_openconfig_platform_components ${img-path} ${__full_line} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-img.py get_openconfig_platform_components ${img-name} ${__full_line} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-img.py get_openconfig_platform_components ${img-name} ${__full_line} + + + + diff --git a/src/CLI/clitree/cli-xml/include/pipe.xml b/src/CLI/clitree/cli-xml/include/pipe.xml new file mode 100644 index 0000000000..758ae50897 --- /dev/null +++ b/src/CLI/clitree/cli-xml/include/pipe.xml @@ -0,0 +1,501 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clitree/cli-xml/include/pipe_without_display_xml.xml b/src/CLI/clitree/cli-xml/include/pipe_without_display_xml.xml new file mode 100644 index 0000000000..50f99bf013 --- /dev/null +++ b/src/CLI/clitree/cli-xml/include/pipe_without_display_xml.xml @@ -0,0 +1,482 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clitree/cli-xml/interface.xml b/src/CLI/clitree/cli-xml/interface.xml new file mode 100644 index 0000000000..e9c08ec7d7 --- /dev/null +++ b/src/CLI/clitree/cli-xml/interface.xml @@ -0,0 +1,767 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + if test "${id}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-vlan.py get_sonic_vlan_sonic_vlan "" show_vlan.j2 ${__full_line} + else + python $SONIC_CLI_ROOT/sonic-cli-vlan.py get_sonic_vlan_sonic_vlan Vlan${id} show_vlan.j2 ${__full_line} + fi + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-portchannel.py get_sonic_portchannel_sonic_portchannel_lag_table show_portchannel.j2 ${__full_line} + + + + + + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface Vlan${vlan-id} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface PortChannel${lag-id} + + + + + + + + + + + + + + if test "${if-subcommands}" = "PortChannel"; then + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_interfaces_interfaces_interface PortChannel${lag-id} + fi + if test "${if-subcommands}" = "Vlan"; then + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_interfaces_interfaces_interface Vlan${vlan-id} + fi + + + + + + + + + + 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 + + + + + + + + + + + + + + + + + + + + + + if test "${switchport-subcommands}" = "access"; then + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_vlan_interfaces_interface_ethernet_switched_vlan_config ${iface} ACCESS ${vlan-id} + else + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_vlan_interfaces_interface_ethernet_switched_vlan_config ${iface} TRUNK ${vlan-id} + fi + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_sample_rate ${iface} ${rate} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_admin_state ${iface} up + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_admin_state ${iface} down + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py delete_sonic_sflow_sonic_sflow_sflow_session_sflow_session_list_sample_rate ${iface} + + + + + + + + + + + + + + + + + + + + + if test "${switchport-subcommands}" = "access"; then + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_vlan_interfaces_interface_ethernet_switched_vlan_config_access_vlan ${iface} ACCESS + else + python $SONIC_CLI_ROOT/sonic-cli-if.py del_llist_openconfig_vlan_interfaces_interface_ethernet_switched_vlan_config_trunk_vlans ${iface} TRUNK ${vlan-id} + fi + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${vlan_id} ${mtu} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${vlan_id} 9100 + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py delete_sonic_vxlan_sonic_vxlan_suppress_vlan_neigh_suppress_vlan_neigh_list ${vlan_id} + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py patch_list_sonic_vxlan_sonic_vxlan_suppress_vlan_neigh_suppress_vlan_neigh_list ${vlan_id} + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${vlan_id} False + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${vlan_id} True + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_aggregate_interfaces_interface_aggregation_config_min_links ${po_name} ${min-links} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_aggregate_interfaces_interface_aggregation_config_min_links ${po_name} 0 + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${po_name} ${mtu} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_mtu ${po_name} 9100 + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${po_name} False + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${po_name} True + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv4_addresses_address_config ${po_name} ${addr} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv4_addresses_address_config_prefix_length ${po_name} ${addr} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_dell_intf_augments_interfaces_interface_aggregation_config_fallback ${po_name} True + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_dell_intf_augments_interfaces_interface_aggregation_config_fallback ${po_name} False + + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_interface_mclag_interface_list ${domain_id} ${po_name} + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_interface_mclag_interface_list ${domain_id} ${po_name} + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${iface} False + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_enabled ${iface} True + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_description ${iface} ${desc} + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_interfaces_interfaces_interface_config_description ${iface} "" + + + + 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 new file mode 100644 index 0000000000..1d974a54b2 --- /dev/null +++ b/src/CLI/clitree/cli-xml/ip.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + diff --git a/src/CLI/clitree/cli-xml/ipv4.xml b/src/CLI/clitree/cli-xml/ipv4.xml new file mode 100644 index 0000000000..e66f3c56a7 --- /dev/null +++ b/src/CLI/clitree/cli-xml/ipv4.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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} + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..30a1a3dcf2 --- /dev/null +++ b/src/CLI/clitree/cli-xml/ipv6.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + 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} + + + + + + + + + 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} + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py patch_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv6_addresses_address_config ${po_name} ${addr} + + + + + python $SONIC_CLI_ROOT/sonic-cli-if.py delete_openconfig_if_ip_interfaces_interface_subinterfaces_subinterface_ipv6_addresses_address_config_prefix_length ${po_name} ${addr} + + + + diff --git a/src/CLI/clitree/cli-xml/lldp.xml b/src/CLI/clitree/cli-xml/lldp.xml new file mode 100644 index 0000000000..d03352e24b --- /dev/null +++ b/src/CLI/clitree/cli-xml/lldp.xml @@ -0,0 +1,54 @@ + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-lldp.py get_openconfig_lldp_lldp_interfaces lldp_show.j2 ${__full_line} + + + python $SONIC_CLI_ROOT/sonic-cli-lldp.py get_openconfig_lldp_lldp_interfaces lldp_show.j2 ${__full_line} + + + + + + if test "${ifname}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-lldp.py get_openconfig_lldp_lldp_interfaces lldp_neighbor_show.j2 ${__full_line} + else + python $SONIC_CLI_ROOT/sonic-cli-lldp.py get_openconfig_lldp_lldp_interfaces_interface lldp_neighbor_show.j2 ${ifname} ${__full_line} + fi + + + + + + + diff --git a/src/CLI/clitree/cli-xml/mac.xml b/src/CLI/clitree/cli-xml/mac.xml new file mode 100644 index 0000000000..17ce13c96c --- /dev/null +++ b/src/CLI/clitree/cli-xml/mac.xml @@ -0,0 +1,324 @@ + + + + + + + + + + + builtin="clish_nop" + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 show + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac_count.j2 count + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 mac-addr ${mac-addr} + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 vlan ${vlan-id} + + + + + + + + + + + + + + + + + if test "${if-subcommands}" = "Ethernet"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 interface Ethernet${phy-if-id} + elif test "${if-subcommands}" = "PortChannel"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 interface PortChannel${port-channel-id} + else + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 ${__full_line} + fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${s-subcommands}" = "address"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 static address ${mac-addr} + elif test "${s-subcommands}" = "Vlan"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 static vlan ${vlan-id} + elif test "${s-subcommands}" = "interface"; then + if test "${interface-subcommand}" = "PortChannel"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 static interface PortChannel${port-channel-id} + else + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 static interface Ethernet${phy-if-id} + fi + else + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 static ${__full_line} + fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${d-subcommands}" = "address"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 dynamic address ${mac-addr} + elif test "${d-subcommands}" = "Vlan"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 dynamic vlan ${vlan-id} + elif test "${d-subcommands}" = "interface"; then + if test "${interface-subcommand}" = "PortChannel"; then + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 dynamic interface PortChannel${port-channel-id} + else + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 dynamic interface Ethernet${phy-if-id} + fi + else + python $SONIC_CLI_ROOT/sonic-cli-mac.py get_openconfig_network_instance_network_instances_network_instance_fdb_mac_table_entries show_mac.j2 dynamic ${__full_line} + fi + + + + + diff --git a/src/CLI/clitree/cli-xml/mclag.xml b/src/CLI/clitree/cli-xml/mclag.xml new file mode 100644 index 0000000000..84d7fca518 --- /dev/null +++ b/src/CLI/clitree/cli-xml/mclag.xml @@ -0,0 +1,103 @@ + + + + + + +]> + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list ${mclag-domain-id} + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_source_ip ${id} ${SIP} + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_source_ip ${id} + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_ip ${id} ${PIP} + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_ip ${id} + + + + + + + + + + + + if test "${if-subcommands}" = "PortChannel"; then + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_link ${id} PortChannel${PLK} + else + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_link ${id} Ethernet${PLK} + fi + + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_peer_link ${id} + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_keepalive_interval ${id} ${KA} + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_keepalive_interval ${id} + + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py patch_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_session_timeout ${id} ${ST} + + + python $SONIC_CLI_ROOT/sonic-cli-mclag.py delete_sonic_mclag_sonic_mclag_mclag_domain_mclag_domain_list_session_timeout ${id} + + + diff --git a/src/CLI/clitree/cli-xml/mirror.xml b/src/CLI/clitree/cli-xml/mirror.xml new file mode 100755 index 0000000000..9989e18d3b --- /dev/null +++ b/src/CLI/clitree/cli-xml/mirror.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + if test "${session-name}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -show + else + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -show --session ${session-name} + fi + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -clear --session ${session-name} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${dst_ip}" != ""; then + if test "${src-phy-if-id}" = ""; then + if test "${ip_gre}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -config --session ${session_name} --dst_ip ${dst_ip} --src_ip ${src_ip} --dscp ${ip_dscp} --ttl ${ip_ttl} + else + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -config --session ${session_name} --dst_ip ${dst_ip} --src_ip ${src_ip} --dscp ${ip_dscp} --gre ${ip_gre} --ttl ${ip_ttl} + fi + else + if test "${ip_gre}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -config --session ${session_name} --dst_ip ${dst_ip} --src_ip ${src_ip} --dscp ${ip_dscp} --ttl ${ip_ttl} --source ${src-phy-if-id} --direction ${sess-direction} + else + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -config --session ${session_name} --dst_ip ${dst_ip} --src_ip ${src_ip} --dscp ${ip_dscp} --gre ${ip_gre} --ttl ${ip_ttl} --source ${src-phy-if-id} --direction ${sess-direction} + fi + fi + elif test "${phy-if-id}" != ""; then + if test "${src-phy-if-id}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -config --session ${session_name} --destination ${phy-if-id} + else + python $SONIC_CLI_ROOT/sonic-cli-mirror.py -config --session ${session_name} --destination ${phy-if-id} --source ${src-phy-if-id} --direction ${sess-direction} + fi + fi + + + + + diff --git a/src/CLI/clitree/cli-xml/platform.xml b/src/CLI/clitree/cli-xml/platform.xml new file mode 100644 index 0000000000..1ab3f0400a --- /dev/null +++ b/src/CLI/clitree/cli-xml/platform.xml @@ -0,0 +1,50 @@ + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-pfm.py get_openconfig_platform_components platform_show.j2 ${__full_line} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-pfm.py get_openconfig_platform_components platform_show.j2 ${__full_line} + + + + + + + diff --git a/src/CLI/clitree/cli-xml/ptp.xml b/src/CLI/clitree/cli-xml/ptp.xml new file mode 100644 index 0000000000..db1ce23880 --- /dev/null +++ b/src/CLI/clitree/cli-xml/ptp.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + if test "${ptp-subcommands}" = "time-property"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py get_ietf_ptp_ptp_instance_list_time_properties_ds 0 ptp_tp_show.j2 + elif test "${ptp-subcommands}" = "clock"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py get_ietf_ptp_ptp_instance_list_default_ds 0 ptp_clock_show.j2 + elif test "${ptp-subcommands}" = "parent"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py get_ietf_ptp_ptp_instance_list_parent_ds 0 ptp_parent_show.j2 + elif test "${ptp-subcommands}" = "port"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py get_ietf_ptp_ptp_instance_list_port_ds_list 0 ${ptp_port_number} ptp_port_show.j2 + elif test "${ptp-subcommands}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py get_ietf_ptp_ptp_instance_list 0 ptp_show.j2 + else + python $SONIC_CLI_ROOT/sonic-cli-ptp.py get_ietf_ptp_ptp_instance_list 0 ptp_port_show.j2 + fi + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${cfg-ptp-subcommands}" = "mode"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "clock-type" ${mode_type} + elif test "${cfg-ptp-subcommands}" = "delay-mechanism"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py patch_ietf_ptp_ptp_transparent_clock_default_ds_delay_mechanism ${ptp_delay_mechanism_type} + elif test "${cfg-ptp-subcommands}" = "network-transport"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "network-transport" ${ptp_network_transport_type} + elif test "${cfg-ptp-subcommands}" = "domain"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py patch_ietf_ptp_ptp_instance_list_default_ds_domain_number 0 ${ptp_domain} + elif test "${cfg-ptp-subcommands}" = "domain-profile"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "domain-profile" ${ptp_domain_profile} + elif test "${cfg-ptp-subcommands}" = "two-step"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py patch_ietf_ptp_ptp_instance_list_default_ds_two_step_flag 0 ${ptp_two_step} + elif test "${cfg-ptp-subcommands}" = "priority1"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py patch_ietf_ptp_ptp_instance_list_default_ds_priority1 0 ${ptp_priority1} + elif test "${cfg-ptp-subcommands}" = "priority2"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py patch_ietf_ptp_ptp_instance_list_default_ds_priority2 0 ${ptp_priority2} + elif test "${cfg-ptp-subcommands}" = "log-announce-interval"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "log-announce-interval" ${ptp_announce_interval} + elif test "${cfg-ptp-subcommands}" = "announce-timeout"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "announce-receipt-timeout" ${ptp_announce_timeout} + elif test "${cfg-ptp-subcommands}" = "log-sync-interval"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "log-sync-interval" ${ptp_sync_interval} + elif test "${cfg-ptp-subcommands}" = "log-min-delay-req-interval"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "log-min-delay-req-interval" ${ptp_delay_request_interval} + elif test "${cfg-ptp-subcommands}" = "port"; then + if test "${add}" = "add"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "add_port" ${Ethernet}${ptp_port_number} + elif test "${del}" = "del"; then + python $SONIC_CLI_ROOT/sonic-cli-ptp.py "del_port" ${Ethernet}${ptp_port_number} + fi + fi + + + + diff --git a/src/CLI/clitree/cli-xml/sflow.xml b/src/CLI/clitree/cli-xml/sflow.xml new file mode 100644 index 0000000000..7109b35127 --- /dev/null +++ b/src/CLI/clitree/cli-xml/sflow.xml @@ -0,0 +1,65 @@ + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py get_sonic_sflow_sonic_sflow show_sflow.j2 ${__full_line} + + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py get_sonic_sflow_sonic_sflow_sflow_session_table show_sflow_intf.j2 ${__full_line} + + + + + + + + + + + if test "${port}" = ""; then python $SONIC_CLI_ROOT/sonic-cli-sflow.py put_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list ${name} ${ip}; else python $SONIC_CLI_ROOT/sonic-cli-sflow.py put_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list ${name} ${ip} ${port}; fi + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_sflow_list_admin_state up + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id ${interface} + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval ${interval} + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py patch_sonic_sflow_sonic_sflow_sflow_sflow_list_admin_state down + + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py delete_sonic_sflow_sonic_sflow_sflow_collector_sflow_collector_list ${name} + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py delete_sonic_sflow_sonic_sflow_sflow_sflow_list_agent_id + + + python $SONIC_CLI_ROOT/sonic-cli-sflow.py delete_sonic_sflow_sonic_sflow_sflow_sflow_list_polling_interval + + + diff --git a/src/CLI/clitree/cli-xml/sonic_types.xml b/src/CLI/clitree/cli-xml/sonic_types.xml new file mode 100644 index 0000000000..9473daf332 --- /dev/null +++ b/src/CLI/clitree/cli-xml/sonic_types.xml @@ -0,0 +1,586 @@ + + + + + + + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clitree/cli-xml/startup.xml b/src/CLI/clitree/cli-xml/startup.xml new file mode 100644 index 0000000000..825fceb4a4 --- /dev/null +++ b/src/CLI/clitree/cli-xml/startup.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/src/CLI/clitree/cli-xml/stp.xml b/src/CLI/clitree/cli-xml/stp.xml new file mode 100644 index 0000000000..2d0953fa9a --- /dev/null +++ b/src/CLI/clitree/cli-xml/stp.xml @@ -0,0 +1,1071 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${vlan}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp show_stp.j2 + elif test "${name}" = "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp_vlan show_stp.j2 ${vlan-id} + else + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp_vlan_interfaces_interface show_stp.j2 ${vlan-id} ${name} + fi + + + + + + + + + + if test "${vlan}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp_counters show_stp_counters.j2 + else + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp_counters_vlan show_stp_counters.j2 ${vlan-id} + fi + builtin="clish_nop" + + + + + + + + + + if test "${vlan}" = ""; then + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp_inconsistentports show_stp_root_guard.j2 + else + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_custom_stp_inconsistentports_vlan show_stp_root_guard.j2 ${vlan-id} + fi + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py get_openconfig_spanning_tree_stp_interfaces show_stp_bpdu_guard.j2 + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_global_config_bpdu_filter True + + + + + + + + + + + + if test "${mode-subcmds}" = "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py post_openconfig_spanning_tree_stp_global_config_enabled_protocol "pvst" + else + python $SONIC_CLI_ROOT/sonic-cli-stp.py post_openconfig_spanning_tree_stp_global_config_enabled_protocol ${__params} + fi + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${stp-vlan-subcmds}" = "forward-time";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_config_forwarding_delay ${vlan-range} ${seconds} + elif test "${stp-vlan-subcmds}" = "hello-time";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_config_hello_time ${vlan-range} ${seconds} + elif test "${stp-vlan-subcmds}" = "max-age";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_config_max_age ${vlan-range} ${seconds} + elif test "${stp-vlan-subcmds}" = "priority";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_config_bridge_priority ${vlan-range} ${value} + elif test "${stp-vlan-subcmds}" = "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_config_enable ${vlan-range} True + fi + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_global_config_forwarding_delay ${seconds} + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_global_config_rootguard_timeout ${value} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_global_config_hello_time ${seconds} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_global_config_max_age ${seconds} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_global_config_bridge_priority ${value} + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_global_config_bpdu_filter False + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_stp_global_config_enabled_protocol + + + + + + + + + + + + + + + + + + + + if test "${stp-vlan-subcmds}" = "forward-time";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_custom_stp_vlan_config_forwarding_delay ${vlan-range} + elif test "${stp-vlan-subcmds}" = "hello-time";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_custom_stp_vlan_config_hello_time ${vlan-range} + elif test "${stp-vlan-subcmds}" = "max-age";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_custom_stp_vlan_config_max_age ${vlan-range} + elif test "${stp-vlan-subcmds}" = "priority";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_custom_stp_vlan_config_bridge_priority ${vlan-range} + elif test "${stp-vlan-subcmds}" = "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_config_enable ${vlan-range} False + fi + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_global_config_forwarding_delay + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_global_config_rootguard_timeout + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_global_config_hello_time + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_global_config_max_age + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_global_config_bridge_priority + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${stp-if-subcmds}" = "bpduguard";then + if test "${port-shutdown}" != "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_bpdu_guard_port_shutdown ${iface} True + else + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_guard ${iface} True + fi + elif test "${stp-if-subcmds}" = "portfast";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_portfast ${iface} True + elif test "${stp-if-subcmds}" = "uplinkfast";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_portfast ${iface} True + elif test "${stp-if-subcmds}" = "enable";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_spanning_tree_enable ${iface} True + elif test "${stp-if-subcmds}" = "cost";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_cost ${iface} ${value} + elif test "${stp-if-subcmds}" = "port-priority";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_port_priority ${iface} ${value} + elif test "${stp-if-subcmds}" = "link-type";then + if test "${link-sub-cmds}" = "auto";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_stp_interfaces_interface_config_link_type ${iface} + elif test "${link-sub-cmds}" = "point-to-point";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_link_type ${iface} "P2P" + elif test "${link-sub-cmds}" = "shared";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_link_type ${iface} "SHARED" + fi + elif test "${stp-if-subcmds}" = "port";then + if test "${edge}" != "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_edge_port ${iface} True + fi + fi + builtin="clish_nop" + + + + + + + + + + + + if test "${bpdufilt_sub_cmds}" = "enable";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_filter ${iface} True + elif test "${bpdufilt_sub_cmds}" = "disable";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_filter ${iface} False + fi + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_guard ${iface} "ROOT" + + + + + + + + + + + + + + + + + + + + + if test "${stp-if-vlan-subcmds}" = "cost";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_interfaces_interface_config_cost ${vlan-range} ${iface} ${value} + elif test "${stp-if-vlan-subcmds}" = "port-priority";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_custom_stp_vlan_interfaces_interface_config_port_priority ${vlan-range} ${iface} ${value} + fi + builtin="clish_nop" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${stp-if-subcmds}" = "bpduguard";then + if test "${port-shutdown}" != "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_bpdu_guard_port_shutdown ${iface} False + else + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_guard ${iface} False + fi + elif test "${stp-if-subcmds}" = "uplinkfast";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_uplink_fast ${iface} False + elif test "${stp-if-subcmds}" = "bpdufilter";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_stp_interfaces_interface_config_bpdu_filter ${iface} + elif test "${stp-if-subcmds}" = "portfast";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_portfast ${iface} False + elif test "${stp-if-subcmds}" = "enable";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_ext_stp_interfaces_interface_config_spanning_tree_enable ${iface} False + elif test "${stp-if-subcmds}" = "cost";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_interfaces_interface_config_cost ${iface} + elif test "${stp-if-subcmds}" = "port-priority";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_ext_stp_interfaces_interface_config_port_priority ${iface} + elif test "${stp-if-subcmds}" = "link-type";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_openconfig_spanning_tree_stp_interfaces_interface_config_link_type ${iface} + elif test "${stp-if-subcmds}" = "port";then + if test "${type}" != "";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_edge_port ${iface} False + fi + fi + builtin="clish_nop" + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-stp.py patch_openconfig_spanning_tree_stp_interfaces_interface_config_guard ${iface} "NONE" + + + + + + + + + + + + + + + + if test "${stp-if-vlan-subcmds}" = "cost";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_custom_stp_vlan_interfaces_interface_config_cost ${vlan-range} ${iface} + elif test "${stp-if-vlan-subcmds}" = "port-priority";then + python $SONIC_CLI_ROOT/sonic-cli-stp.py delete_custom_stp_vlan_interfaces_interface_config_port_priority ${vlan-range} ${iface} + fi + builtin="clish_nop" + + + + + + diff --git a/src/CLI/clitree/cli-xml/system.xml b/src/CLI/clitree/cli-xml/system.xml new file mode 100644 index 0000000000..09dd7b5d21 --- /dev/null +++ b/src/CLI/clitree/cli-xml/system.xml @@ -0,0 +1,80 @@ + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sys.py get_openconfig_system_system_state system_show.j2 ${__full_line} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sys.py get_openconfig_system_system_memory system_show.j2 ${__full_line} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sys.py get_openconfig_system_system_cpus system_cpu_show.j2 ${__full_line} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sys.py get_openconfig_system_system_processes system_processes_show.j2 ${__full_line} + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-sys.py get_openconfig_system_system_processes system_show.j2 ${pid-no} ${__full_line} + + + + diff --git a/src/CLI/clitree/cli-xml/tam.xml b/src/CLI/clitree/cli-xml/tam.xml new file mode 100755 index 0000000000..f73750fa86 --- /dev/null +++ b/src/CLI/clitree/cli-xml/tam.xml @@ -0,0 +1,157 @@ + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + if test "${tam-subcommands}" = "collector"; then + python $SONIC_CLI_ROOT/tam.py -show --collector ${collector-name} -templ -showcollector.j2; + elif test "${tam-subcommands}" = "device"; then + python $SONIC_CLI_ROOT/tam.py -show -device; + fi + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/tam.py -clear -device -deviceid 0 + + + + + + + + python $SONIC_CLI_ROOT/tam.py -clear --collector ${collector-name} + + + + + + + + + python $SONIC_CLI_ROOT/tam.py -config -device -deviceid ${device-id-value} + + + + + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/tam.py -config --collector ${collector-name} --iptype ${type-name} --ipaddr ${ipv4-type} --port ${collector-port} + + + + + + + diff --git a/src/CLI/clitree/cli-xml/ts.xml b/src/CLI/clitree/cli-xml/ts.xml new file mode 100755 index 0000000000..f7dd941d16 --- /dev/null +++ b/src/CLI/clitree/cli-xml/ts.xml @@ -0,0 +1,172 @@ + + + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + if test "${tam-int-ifa-ts-subcommands}" = "supported"; then + python $SONIC_CLI_ROOT/ts.py -show -supported -templ -showsupported.j2; + elif test "${tam-int-ifa-ts-subcommands}" = "status"; then + python $SONIC_CLI_ROOT/ts.py -show -status -templ -showstatus.j2; + elif test "${tam-int-ifa-ts-subcommands}" = "flow"; then + python $SONIC_CLI_ROOT/ts.py -show -flow ${flow-name} -templ -showflow.j2; + elif test "${tam-int-ifa-ts-subcommands}" = "statistics"; then + python $SONIC_CLI_ROOT/ts.py -show -statistics -flow ${flow-name} -templ -showstatisticsflow.j2; + fi + + + + + + + + + + + + + + + + + + + + + if test "${flow-options}" = "flow-name"; then + python $SONIC_CLI_ROOT/ts.py -clear -flow ${flow-name}; elif test "${flow-options}" = "all"; then + python $SONIC_CLI_ROOT/ts.py -clear -flow all; fi + + + + + + + + + + + + + if test "${feature-options}" = "enable"; then + python $SONIC_CLI_ROOT/ts.py -config -enable; + elif test "${feature-options}" = "disable"; then + python $SONIC_CLI_ROOT/ts.py -config -disable; + fi + + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/ts.py -config -flow ${flow-name} -acl_table ${acl-table-name} -acl_rule ${acl-rule-name} + + + + + diff --git a/src/CLI/clitree/cli-xml/udld.xml b/src/CLI/clitree/cli-xml/udld.xml new file mode 100755 index 0000000000..d1abc7070d --- /dev/null +++ b/src/CLI/clitree/cli-xml/udld.xml @@ -0,0 +1,222 @@ + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py get_sonic_udld_sonic_udld_udld_udld_list show_udld_global.j2 + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py get_list_sonic_udld_sonic_udld_udld_port_neigh_table_udld_port_neigh_table_list show_udld_neighbor.j2 + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py get_sonic_udld_sonic_udld_udld_port_neigh_table_udld_port_neigh_table_list show_udld_interface.j2 ${interface-name} + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py get_list_sonic_udld_sonic_udld_udld_port_table_udld_port_table_list show_udld_stats.j2 + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py get_sonic_udld_sonic_udld_udld_port_table_udld_port_table_list show_udld_stats.j2 ${interface-name} + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py udldGlobalDebugHandler 1 + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py udldGlobalDebugHandler 0 + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py udldInterfaceDebugHandler 1 ${interface-name} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py udldInterfaceDebugHandler 0 ${interface-name} + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py udldInterfaceCountersClearHandler + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py udldInterfaceCountersClearHandler ${interface-name} + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py post_list_sonic_udld_sonic_udld_udld_udld_list + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py delete_sonic_udld_sonic_udld_udld + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_udld_list_aggressive true + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_udld_list_aggressive false + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_udld_list_msg_time ${msg-time} + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_udld_list_msg_time 0 + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_udld_list_multiplier ${multiplier} + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_udld_list_multiplier 0 + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py post_list_sonic_udld_sonic_udld_udld_port_udld_port_list ${iface} + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py delete_sonic_udld_sonic_udld_udld_port_udld_port_list ${iface} + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_port_udld_port_list_aggressive true ${iface} + + + + + python $SONIC_CLI_ROOT/sonic-cli-udld.py patch_sonic_udld_sonic_udld_udld_port_udld_port_list_aggressive false ${iface} + + + + diff --git a/src/CLI/clitree/cli-xml/vrf.xml b/src/CLI/clitree/cli-xml/vrf.xml new file mode 100644 index 0000000000..8ecf4aa920 --- /dev/null +++ b/src/CLI/clitree/cli-xml/vrf.xml @@ -0,0 +1,57 @@ + + + + + + + + python $SONIC_CLI_ROOT/openconfig_network_instance_api.py get_list_openconfig_network_instance_network_instances_network_instance show_mgmt_vrf.j2 + + + + + python $SONIC_CLI_ROOT/openconfig_network_instance_api.py get_openconfig_network_instance_network_instances_network_instance "mgmt" show_mgmt_vrf.j2 + + + + + + + + + + python $SONIC_CLI_ROOT/openconfig_network_instance_api.py patch_openconfig_network_instance_network_instances_network_instance "mgmt" "L3VRF" true + + + + + + + + python $SONIC_CLI_ROOT/openconfig_network_instance_api.py delete_openconfig_network_instance_network_instances_network_instance "mgmt" "L3VRF" true + + + + + + diff --git a/src/CLI/clitree/cli-xml/vxlan.xml b/src/CLI/clitree/cli-xml/vxlan.xml new file mode 100644 index 0000000000..fe44328284 --- /dev/null +++ b/src/CLI/clitree/cli-xml/vxlan.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py delete_sonic_vxlan_sonic_vxlan_evpn_nvo ${iface} + + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py patch_list_sonic_vxlan_sonic_vxlan_evpn_nvo_evpn_nvo_list ${iface} ${vxlan_name} + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py delete_sonic_vxlan_sonic_vxlan_vxlan_tunnel ${iface} + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py delete_sonic_vxlan_sonic_vxlan_vxlan_tunnel_map_vxlan_tunnel_map_list ${iface} ${vnid} ${vid} + + + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py patch_list_sonic_vxlan_sonic_vxlan_vxlan_tunnel_vxlan_tunnel_list ${iface} ${SIP} + + + + + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-vxlan.py patch_list_sonic_vxlan_sonic_vxlan_vxlan_tunnel_map_vxlan_tunnel_map_list ${iface} ${vnid} ${vid} + + + + + diff --git a/src/CLI/clitree/cli-xml/ztp.xml b/src/CLI/clitree/cli-xml/ztp.xml new file mode 100755 index 0000000000..f83c75ea82 --- /dev/null +++ b/src/CLI/clitree/cli-xml/ztp.xml @@ -0,0 +1,44 @@ + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-ztp.py get_openconfig_platform_components show_ztp.j2 ${__full_line} + + + + + + + + + python $SONIC_CLI_ROOT/sonic-cli-ztp.py get_openconfig_platform_components ${__full_line} + + + + + python $SONIC_CLI_ROOT/sonic-cli-ztp.py get_openconfig_platform_components ${__full_line} + + + + diff --git a/src/CLI/clitree/macro/acl_macro.xml b/src/CLI/clitree/macro/acl_macro.xml new file mode 100644 index 0000000000..329717c992 --- /dev/null +++ b/src/CLI/clitree/macro/acl_macro.xml @@ -0,0 +1,304 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/clitree/scripts/klish_ins_def_cmd.py b/src/CLI/clitree/scripts/klish_ins_def_cmd.py new file mode 100755 index 0000000000..01032c07b6 --- /dev/null +++ b/src/CLI/clitree/scripts/klish_ins_def_cmd.py @@ -0,0 +1,187 @@ +#!/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 + in list SKIP_VIEW_LIST + + The script accepts input directory and output directory as parameters. + It reads each XML file in the input directory and iterates through + each VIEW tag in the XML.If "exit" and "end" command are not already + appended for the view,the script will append them for the view. + + On successful iteration through all the views, the resultant XML tree is + written to a file in the output directory with same name as in source + directory. + + Script maintains a list VIEW_LIST to hold the list of views for which the + exit and end commands are updated already. + + Note that a view could be present in multiple files.Appending in one of + the files for a view is enough for the command to appear for that mode. + + A special list SKIP_VIEW_LIST is being maintained which holds the + list of views for which we don't want the exit and end command to be + appended (Eg enable-view") + + The SKIP_VIEW_LIST of the script should be updated with any new view being + created for which we don't want exit and end command + + Usage: klish_ins_def_cmd.py inDir [outDir] [--debug]""" + +import sys +import os +from lxml import etree + +EXIT_CMD = """ + + """ + +COMMENT_NS = """""" + +INHERIT_ENABLE_MODE_CMD = """""" + +""" Bring all enable mode commands to config + modes directly (Commands hidden to the user) + so that all enable mode commands can be + executed from config mode itself.""" + +INHERIT_ENABLE_MODE_CMD_WITHOUT_PREFIX = """""" + + +END_CMD = """""" + +VIEW_TAG_STR = """{http://www.dellemc.com/sonic/XMLSchema}VIEW""" +ENABLE_VIEW_STR = """enable-view""" +SKIP_VIEW_LIST = ["enable-view", "hidden-view", "ping-view"] +#DBG_FLAG = False +DBG_FLAG = True + + + +def update_view_tag(root, viewlist, filename, out_dirpath): + + """ The function iterates through the VIEW tags in the + file,and appends exit and end commands to the view + if not added already""" + + out_file = out_dirpath+'/'+filename + file_modified = False + + for element_inst in root.iter(VIEW_TAG_STR): + if DBG_FLAG == True: + print "Processed view name %s" % str(element_inst.keys()) + if (element_inst.get('prompt') != None) and (element_inst.get('name') not in SKIP_VIEW_LIST): + view_name = element_inst.get('name') + + if view_name not in viewlist: + exit_element = etree.XML(EXIT_CMD) + end_element = etree.XML(END_CMD) + inherit_enable_element = etree.XML(INHERIT_ENABLE_MODE_CMD) + inherit_enable_element_without_prefix = etree.XML(INHERIT_ENABLE_MODE_CMD_WITHOUT_PREFIX) + comment_element = etree.XML(COMMENT_NS) + + if DBG_FLAG == True: + print "Appending to view %s ..." %view_name + element_inst.insert(0,end_element) + element_inst.insert(0,exit_element) + element_inst.insert(0,inherit_enable_element) + element_inst.insert(0,inherit_enable_element_without_prefix) + element_inst.insert(0,comment_element) + + file_modified = True + viewlist.append(view_name) + + if DBG_FLAG == True: + print etree.tostring(element_inst, pretty_print=True) + print "VIEW_LIST:" + # print VIEW_LIST + + if file_modified == True: + if DBG_FLAG == True: + print "Writing File %s ..." %out_file + root.write(out_file, xml_declaration=True, encoding=root.docinfo.encoding, pretty_print=True) + else: + + if DBG_FLAG == True: + print "Skipping File %s ..." %filename + return viewlist + +def ins_def_cmd (in_dirpath, out_dirpath, debug): + IN_DIR_PATH = in_dirpath + VIEW_LIST = [] + DBG_FLAG = debug + + parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) + + for dir_name, subdir_list, file_list in os.walk(IN_DIR_PATH): + for fname in file_list: + if fname.endswith(".xml"): + if DBG_FLAG == True: + print '\tInput File:%s' % fname + tree = etree.parse(dir_name+'/'+fname, parser) + VIEW_LIST = update_view_tag(tree, VIEW_LIST, fname, out_dirpath) + +if __name__ == "__main__": + + debug = False + if len(sys.argv) < 2: + print ("Error: Missing Parameter " + os.linesep + + "Usage: klish_ins_def_cmd.py inDir [outDir] [--debug]") + sys.exit(0) + + if sys.argv[1] == "--help": + print "Usage: klish_ins_def_cmd.py inDir [outDir] [--debug]" + sys.exit(0) + + if len(sys.argv) < 3: + out_dirpath = sys.argv[1] + elif sys.argv[2] == "--debug": + out_dirpath = sys.argv[1] + else: + out_dirpath = sys.argv[2] + + if len(sys.argv) < 3: + debug = False + elif sys.argv[2] == "--debug": + debug = True + + if (len(sys.argv) == 4) and (sys.argv[3] == "--debug"): + debug = True + + debug = True + print sys.argv[1], out_dirpath, 1 + ins_def_cmd (sys.argv[1], out_dirpath, debug) + + diff --git a/src/CLI/clitree/scripts/klish_insert_pipe.py b/src/CLI/clitree/scripts/klish_insert_pipe.py new file mode 100755 index 0000000000..27c074cad3 --- /dev/null +++ b/src/CLI/clitree/scripts/klish_insert_pipe.py @@ -0,0 +1,197 @@ +#!/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 + ----------------------------------------------------------------''' + +import sys +import os +import re +from lxml import etree +import xml.etree.ElementTree as ET + +DBG_FLAG = False +PIPE_XML = "include/pipe.xml" +PIPE_WITHOUT_DISPLAY_XML = "include/pipe_without_display_xml.xml" +DEFAULT_NS_HREF = "http://www.dellemc.com/sonic/XMLSchema" +XSI_NS_HREF = "http://www.dellemc.com/sonic/XMLSchema-instance" +XI_NS_HREF = "http://www.w3.org/2001/XInclude" +COMMAND_XPATH_EXPR = ".//{"+DEFAULT_NS_HREF+"}VIEW/{"+DEFAULT_NS_HREF+"}COMMAND" +HIDDEN_CMD_XPATH_EXPR = ".//{"+DEFAULT_NS_HREF+"}VIEW[@name='hidden-view']/{"+DEFAULT_NS_HREF+"}COMMAND" +ACTION_XPATH_EXPR = "{"+DEFAULT_NS_HREF+"}ACTION" + +# +# *** NOTE : List of action plugins for which display-xml need not be appended *** +# +actionlst = ['clish_history', 'clish_file_print', 'clish_show_alias_plugin', \ + 'clish_logger_on_off', 'clish_show_batch_plugin'] + +''' +@brief Convert the escaped characters back to their original form +@param[in] Input Text +@return Output Text +''' +def unescape(s): + s = re.sub("<", "<", s) + s = re.sub(">", ">", s) + s = re.sub("&", "&", s) + return s + + +''' +@brief Align and Save tempfile to outputfile +@param[in] Temporary file name +@param[in] Output file name +''' +def align_and_save(temp_file_name, output_file_name): + try: + parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) + root = etree.parse(temp_file_name, parser) + text = etree.tostring(root, pretty_print=True, xml_declaration=True, encoding=root.docinfo.encoding) + text = unescape(text) + root.write(output_file_name, pretty_print=True, xml_declaration=True, encoding=root.docinfo.encoding) + except: + error = parser.error_log[0] + print "Error parsing ", os.path.basename(outputfile.name), error.message + print "Error writing ", out_file_name, sys.exc_info()[0] + sys.exit(102) + + +''' +@brief Test whether pipe can be added to given command and insert it +@param[in] COMMAND tag found in xml +@return COMMAND tag with/without pipe sub-element added +''' +def addpipe(command): + splitstr = command.get('name').split() + action = command.find(ACTION_XPATH_EXPR).get('builtin') + if (splitstr[0] == 'show' or splitstr[0] == 'get'): + if action in actionlst: + etree.SubElement(command, "{"+XI_NS_HREF+"}include", href = PIPE_WITHOUT_DISPLAY_XML) + else: + etree.SubElement(command, "{"+XI_NS_HREF+"}include", href = PIPE_XML) + + if DBG_FLAG == True: + print "Adding Pipe for cmd: ", splitstr + return command + + +''' +@brief Register Namespaces so that XPATH expression matches +''' +def registerns(): + ET.register_namespace("", DEFAULT_NS_HREF) + ET.register_namespace("xsi", XSI_NS_HREF) + ET.register_namespace("xi", XI_NS_HREF) + + +''' +@brief Convert the escaped characters back to their original form +@param[in] Input filename +@param[out] Temporary file +''' +def process_input_file(input_file_name, tempfile): + try: + if True: + registerns() + parser = etree.XMLParser(remove_blank_text=True, resolve_entities=False) + tree = etree.parse(input_file_name,parser) + root = tree.getroot() + #root.set("xmlns:" + "xi", XI_NS_HREF) + if DBG_FLAG == True: + print "Root Tag: ",root.tag + for command in root.findall(COMMAND_XPATH_EXPR): + if len(command) != 0: + command = addpipe(command) + for command in root.findall(HIDDEN_CMD_XPATH_EXPR): + if len(command) != 0: + command = addpipe(command) + + tree.write(tempfile, xml_declaration=True, encoding=tree.docinfo.encoding, pretty_print=True) + except IOError as e: + print "Cannot open file: ", e.filename, ":", e.strerror + sys.exit(100) + except : + print "process_input_file:Unknown error: ", sys.exc_info()[0] + sys.exit(100) + +def insert_pipe (dirpath, debug): + + DBG_FLAG = debug + + tmp_dirpath = dirpath + '/tmp' + dirpath = dirpath + "/" + tmp_dirpath = tmp_dirpath + "/" + try: + os.mkdir(tmp_dirpath) + except OSError: + print 'The directory', tmp_dirpath, 'already exists. Using it.' + except: + error = parser.error_log[0] + print "Unknown error", error.message + sys.exit (98) + temp_file_name = tmp_dirpath + "out.xml" + + ''' The following loops go through each directory in the given + input directory and reads each *.xml file in the directory + for inserting the pipe ''' + for fname in os.listdir(dirpath): + fname = dirpath + fname + if not os.path.isfile (fname): + if DBG_FLAG == True: + print 'Skipping', fname, 'since it is not a file' + continue + if DBG_FLAG == True: + print 'Parsing ', fname + if fname.endswith(".xml", re.I): + try: + temp_file = open(temp_file_name, "w") + except IOError as e: + print e.filename, ":", e.strerror + sys.exit(99) + if DBG_FLAG == True: + print fname + process_input_file(fname, temp_file) + temp_file.close() + align_and_save(temp_file_name, fname) + + if os.path.exists(temp_file_name): + os.remove(temp_file_name) + if os.path.exists(tmp_dirpath): + os.rmdir(tmp_dirpath) + +''' +@brief Main Routine to insert pipe for every show and get COMMAND in all + xml files, present in input-dir and save them in output-dir +''' +if __name__ == "__main__": + + if len(sys.argv) == 1 or sys.argv[1] == "--help": + print "Usage:", sys.argv[0], "working-dir [--debug]" + sys.exit(0) + + if len(sys.argv) == 3 and sys.argv[2] == "--debug": + debug = True + else: + debug = False + + insert_pipe (sys.argv[1], debug) + sys.exit(0) + diff --git a/src/CLI/clitree/scripts/klish_platform_features_process.sh b/src/CLI/clitree/scripts/klish_platform_features_process.sh new file mode 100755 index 0000000000..976d55e8e9 --- /dev/null +++ b/src/CLI/clitree/scripts/klish_platform_features_process.sh @@ -0,0 +1,122 @@ +#!/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 +# Create entities_platform.xml and features_platform.xml +# For all platforms_*/xml, run xsltproc $i.xml with entities.xml and features.xsl +# Copy a clish_prepare.py to sysroot +# Run clish_prepare.py with first platform and update entities +# +# Run xsltproc on feature_master.xsd to create an xml file of all features - fullfeatures.xml +# +# During clish start: +# a. Open fullfeatures.xml to populate a list of features vs enabled-flag mappping. +# b. While preparing the pfeature list, consult this list instead of consulting the list of #defines +# c. Report errors on processing the fullfeatures.xml file + +PLATFORM_CONFIGS=platform_dummy.xml +BUILD_DIR=$2/tmp +PARSER_XML_PATH=$2 +ENTITIES_TEMPLATE=$1/mgmt_clish_entities.xsl +FEATURES_MASTER=$1/mgmt_clish_feature_master.xsd + +function insert_in() +{ + # Insert in file - $1 with $value set in calling routine + filename=$1 + outfile=$filename.bak + grep -q DOCTYPE $filename + # If there are ENTITY definitions, add it as part of it + # Else add a new DOCTYPE + if [ $? -eq 0 ]; then + option=1 + matchpattern=".*DOCTYPE.*" + printvalue="${value}" + else + option=2 + matchpattern="" + fi + #echo Insert_in $filename. Option $option + while read -r line; do + echo ${line} >> $outfile + if [[ "${line}" =~ ${matchpattern} ]]; then + #echo Match found for ${line} + echo "${printvalue}" >> $outfile + #set +x + fi + done < $filename + #set -x + touch $outfile $filename + mv -f $outfile $filename +} + +# Do a simple text based insertion of the feature-val entities +# TBD - Replace them with an xml parser based insertion in future, if required +function insert_entities() +{ + value=`cat $1` + parser=$2 + echo insert_entities: $1 $parser + list=`echo ${parser}/*.xml` + for i in ${list}; do + echo Processing $i + insert_in $i + xmllint $i >& /dev/null + if [ $? -ne 0 ]; then + echo ENTITY insertion in $i failed + exit 1 + fi + done + #echo $i + #insert_in $i +} + + +echo Sanity check of platform config files with feature master ... + xmllint --schema $FEATURES_MASTER $PLATFORM_CONFIGS >& /dev/null + if [ $? -ne 0 ]; then + echo Failed to validate $PLATFORM_CONFIGS + exit 1 + fi + +mkdir -p ${BUILD_DIR} +echo Done. Generating platform specific files ... + base=${PLATFORM_CONFIGS%*.xml} # Strip of the .xml suffix + platform=${base#platform_*} # Get the platform name + xsltproc $ENTITIES_TEMPLATE $PLATFORM_CONFIGS > $BUILD_DIR/${platform}_entities.ent #2>/dev/null + # echo ${platform}_entities.ent ready + if [ $? -ne 0 ]; then + echo Failed to apply entities xsl template for $PLATFORM_CONFIGS + exit 1 + fi +echo Done + +# Use the last platform's file for compilation purpose +pwd=${PWD} +cd ${PARSER_XML_PATH} +echo Inserting platform features +insert_entities ${BUILD_DIR}/${platform}_entities.ent ${PARSER_XML_PATH}/command-tree +cp ${BUILD_DIR}/*.xml ${PARSER_XML_PATH}/command-tree + +rm -r ${BUILD_DIR} +exit 0 diff --git a/src/CLI/clitree/scripts/klish_preproc_cmdtree.py b/src/CLI/clitree/scripts/klish_preproc_cmdtree.py new file mode 100755 index 0000000000..140c859f07 --- /dev/null +++ b/src/CLI/clitree/scripts/klish_preproc_cmdtree.py @@ -0,0 +1,58 @@ +#!/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 + b. Platform specific feature xml and feature-val xml creation + c. Insert the |' for post processing support of show commands + d. Insert the default default end and exit command for all config modes + + The Script Usage: + python klish_preproc_cmdtree.py buildpath macros-dir depth +''' +import sys +import os +import re +from lxml import etree +import klish_replace_macro, klish_insert_pipe, klish_ins_def_cmd + +if __name__ == "__main__": + + if len(sys.argv) == 1 or sys.argv[1] == "--help": + print "Usage:", sys.argv[0], "working-dir macrodir nested-macro-levels [--debug]" + sys.exit(0) + + dirpath = sys.argv[1] + macro_dir_path = sys.argv[2] + nested_levels = sys.argv[3] + + if len(sys.argv) == 5 and sys.argv[4] == "--debug": + debug = True + else: + debug = False + + print "Replacing the macros ..." + klish_replace_macro.replace_macros (dirpath, macro_dir_path, nested_levels, debug) + print "Inserting the pipe parameters ..." + klish_insert_pipe.insert_pipe (dirpath, debug) + print "Insert the end, exit commands ..." + klish_ins_def_cmd.ins_def_cmd (dirpath, dirpath, debug) + + diff --git a/src/CLI/clitree/scripts/klish_replace_macro.py b/src/CLI/clitree/scripts/klish_replace_macro.py new file mode 100755 index 0000000000..436b1dea82 --- /dev/null +++ b/src/CLI/clitree/scripts/klish_replace_macro.py @@ -0,0 +1,408 @@ +#!/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. + + The script assumes that xml files using macro's are + kept in some input directory, macro definition files + are kept under another directory and expects a + directory where it keeps all the processed files. + + The script expect that macro definition are kept in a + file with *_macro.xml. + + The Script Usage: + python klish_replace_macro.py indir macrodir outdir [--debug] + + The format requirement for using and defining macro's + are given as follows: + + MACRO Definition file: + example_macro.xml + + + + + + + + + + + + + + + + + + + + + Macro Usage File Example: + + abc.xml + + + + + + + + + + + + ----------------------------------------------------------------''' +import sys +import os +import re +from lxml import etree + +MACRO_START = '' +MACRODEF_START = '' +DBG_FLAG = False + +def align_and_save(temp_file_name, out_file_name, replace_entities): + print "Writing ", out_file_name + try: + parser = etree.XMLParser(remove_blank_text=True, resolve_entities=replace_entities) + root = etree.parse(temp_file_name, parser) + root.write(out_file_name,xml_declaration=True, encoding=root.docinfo.encoding, pretty_print=True) + #root.write(out_file_name,pretty_print=True) + #outputfile.write(etree.tostring(root,xml_declaration=True, encoding=root.docinfo.encoding, pretty_print=True)) + #outputfile.write(etree.tostring(root, pretty_print=True)) + #outputfile.close() + except: + #error = parser.error_log[0] + #print "Error parsing ", os.path.basename(outputfile.name), error.message + print "Error writing ", out_file_name, sys.exc_info() + sys.exit(102) + +def process_spaces(line): + line = re.sub(" =", "=", line) + line = re.sub(" = ", "=", line) + line = re.sub("= ", "=", line) + line = re.sub("< ", "<", line) + line = re.sub(" >", ">", line) + line = re.sub(" />", "/>", line) + line = re.sub(' "', '"', line) + line = re.sub("!=", "!= ", line) + line = re.sub("==", " == ", line) + return line + +def endoflinehandling(line): + if re.search("/>", line, 0) != None: + retstr = re.sub("/>", "", line) + elif re.search(">", line, 0) != None: + retstr = re.sub(">", "", line) + return retstr.strip() +''' +## +# @brief Replace the macro references with the actual macro definition for the +# requested parser xml file +# +# @param macname Name of the macro to be replaced +# @param argcnt Number of arguments in the macro +# @param argval List of argument values in the macro +# @param fd Descriptor for the input xml file where replacement is requested. +# The cursor of fd already points to the place where replacement should +# be done +# @param macro_data List of all macro definitions +# +# @return +''' +def expand_macro(macname, argcnt, argval, fd, macro_data): + matchfound = 0 + try: + macro_start = MACRODEF_START + macname + '>' + gothit = 0 + for macro_line in macro_data: + macro_line = process_spaces(macro_line) + if re.search(macro_start, macro_line, 0) != None: + matchfound = 1 + gothit = 1 + if DBG_FLAG == True: + print "Macro Line Match found", macro_start + continue + else: + if DBG_FLAG == True: + print "macro ***not*** found part" + if macro_line.find(MACRODEF_END) != -1: + matchfound = 0 + continue + if matchfound == 1: + if argcnt == 0: + fd.write(macro_line) + fd.write(" ") + else: + i = 0 + for i in range(argcnt): + param = "arg" + str(i+1) + if macro_line.find(param) != -1: + value = str(argval[i]) + macro_line = re.sub(param, value, macro_line) + + if DBG_FLAG == True: + print "param : " + param + print "argval" + str(i) + ": " + argval[i] + print "macro_line : " + macro_line + fd.write(macro_line) + fd.write(" ") + if gothit == 0: + # A macro match is not found. Possibly a typo in macro name + # Flag fatal error + print "Macro", macname, "not defined in list of macros" + sys.exit(102) + + except: + error = parser.error_log[0] + print "Error expand_macro ", os.path.basename(fd.name), error.message + sys.exit(101) + +''' +## +# @brief Read the requested input file, identify the macro references along +# with the arguments and call expand_macro() for macro replacement +# +# @param filename Input file +# @param fd descriptor of a temporary file +# @param macro_data Array of all the macro definition lines +# +# @return Nothing +''' +def process_input_file(filename, fd, macro_data): + + try: + with open(filename, "r") as input_file: + multi_line = False + macroname = [] + data = input_file.readlines() + for line in data: + nargs = 0 + line = ' '.join(line.split()) + if DBG_FLAG == True: + print line, multi_line + if re.search(MACRO_START, line, 0) != None or multi_line == True: + if DBG_FLAG == True: + print "MACRO is found", line + line = process_spaces(line) + line = line.replace('\,', '#') + if DBG_FLAG == True: + print line + mname = re.sub(MACRO_START, "", line) + if line.find("arg") != -1: + nargs = line.count(',') + 1 + multi_line = False + elif line.find('>') == -1: + multi_line = True + macroname = mname.strip() + if DBG_FLAG == True: + print macroname, multi_line + continue + nargs = int(nargs) + if DBG_FLAG == True: + print nargs + if nargs == 0: + macroname = endoflinehandling(mname) + if DBG_FLAG == True: + print macroname + expand_macro(macroname, 0, None, fd, macro_data) + else: + args = [] + repmname = 'arg="' + if multi_line == False: + if re.search(' ', mname, 0) != None: + macroname = mname[0:mname.find(' ')] + repmname = macroname + ' ' + 'arg="' + if DBG_FLAG == True: + print macroname, nargs + mname = re.sub(repmname, "", mname) + for i in range(nargs): + e = mname.find(',') + if e != -1: + argval = mname[0:e].strip() + mname = mname[e+1:] + else: + e = mname.find('"') + argval = mname[0:e].strip() + argval = argval.replace('#', ',') + args.append(argval) + if DBG_FLAG == True: + print "about to expand_macro", macroname + expand_macro(macroname, nargs, args, fd, macro_data) + elif re.search(MACRO_END, line, 0) != None: + continue + else: + fd.write(line) + fd.write(" ") + fd.close() + input_file.close() + except IOError as e: + print "Cannot open file: ", filename, e.strerror + sys.exit(100) + except : + print "Unknown error: ", sys.exc_info()[0], filename, input_file + sys.exit(100) + +def load_all_macros (macro_dir_path): + ''' Loop through each directory in the given macro directory and + reads each *macro.xml file in the directory into a buffer and return it''' + macro_data = [] + for dirName, subdirList, fileList in os.walk(macro_dir_path): + for macro_file_name in fileList: + macro_file_name = macro_dir_path + "/" + macro_file_name + with open(macro_file_name, "r") as macrofile: + data = macrofile.readlines() + + for line in data: + line = ' '.join(line.split()) + macro_data.append(line) + macrofile.close() + return macro_data + +def process_dir (dirpath, macro_data, replace_entities): + ''' Loop through each file in the given input directory and replace + all macro references with the actual definitions along with + argument substitutions''' + tmp_dirpath = dirpath + '/tmp/' + temp_file_name = tmp_dirpath + "out.xml" + + try: + os.mkdir(tmp_dirpath) + except OSError: + print 'The directory', tmp_dirpath, 'already exists. Using it.' + except: + print "Unknown error" + sys.exit (98) + + if DBG_FLAG == True: + print "process_dir ", dirpath, tmp_dirpath, temp_file_name + + for fname in os.listdir(dirpath): + fname = dirpath + fname + if not os.path.isfile (fname): + if DBG_FLAG == True: + print 'Skipping', fname, 'since it is not a file' + continue + if DBG_FLAG == True: + print 'Parsing ', fname + if fname.endswith(".xml", re.I): + try: + temp_file = open(temp_file_name, "w") + except IOError as e: + print e.filename, ":", e.strerror + sys.exit(99) + if DBG_FLAG == True: + print fname + process_input_file(fname, temp_file, macro_data) + align_and_save(temp_file_name, fname, replace_entities) + temp_file.close() + + if os.path.exists(temp_file_name): + os.remove(temp_file_name) + if os.path.exists(tmp_dirpath): + os.rmdir(tmp_dirpath) + +''' +## +# @brief Resolve all nested MACRO references in the macro definitions +# +# @param macro_dir_path Directory where the macro xml files are defined +# @param nested_levels Maximum nested level of macro references expected. +# Giving a bigger number than the actual nested level is harmless. It +# would just add an additional loop +# +# @return array of all the lines containing the macro defintions +''' +def fix_macros (macro_dir_path, nested_levels): + for i in range(nested_levels): + macro_data = load_all_macros (macro_dir_path) + if DBG_FLAG == True: + print "All macros loaded from", macro_dir_path + process_dir (macro_dir_path, macro_data, True) + + return macro_data + +def replace_macros (dirpath, macro_dir_path, nested_levels, debug): + dirpath = dirpath + "/" + macro_dir_path = macro_dir_path + "/" + DBG_FLAG = debug + + print "Resolving nested macro references in", macro_dir_path + macro_data = fix_macros (macro_dir_path, int(nested_levels)) + + if DBG_FLAG == True: + print macro_data + + print "Processing directory:", dirpath + process_dir (dirpath, macro_data, False) + print "Done" + +if __name__ == "__main__": + + if len(sys.argv) == 1 or sys.argv[1] == "--help": + print "Usage:", sys.argv[0], "working-dir macrodir nested-macro-levels [--debug]" + sys.exit(0) + + dirpath = sys.argv[1] + macro_dir_path = sys.argv[2] + nested_levels = sys.argv[3] + + if len(sys.argv) == 5 and sys.argv[4] == "--debug": + debug = True + else: + debug = False + + replace_macros (dirpath, macro_dir_path, nested_levels, debug) diff --git a/src/CLI/clitree/scripts/platform_dummy.xml b/src/CLI/clitree/scripts/platform_dummy.xml new file mode 100644 index 0000000000..8cf798a09f --- /dev/null +++ b/src/CLI/clitree/scripts/platform_dummy.xml @@ -0,0 +1,40 @@ + + + + + + + NOT_SUPPORTED + + + START_PORT_ID + MAX_PORT_ID + START_SUB_PORT_ID + MAX_SUB_PORT_ID + MAX_MTU + + diff --git a/src/CLI/clitree/scripts/sonic-clish.xsd b/src/CLI/clitree/scripts/sonic-clish.xsd new file mode 100644 index 0000000000..bfae8de77f --- /dev/null +++ b/src/CLI/clitree/scripts/sonic-clish.xsd @@ -0,0 +1,249 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CLI/klish/Makefile b/src/CLI/klish/Makefile new file mode 100644 index 0000000000..c71e96aeaa --- /dev/null +++ b/src/CLI/klish/Makefile @@ -0,0 +1,27 @@ +SHELL = /bin/bash +.ONESHELL: +.SHELLFLAGS += -e + +KLISH_VERSION = 2.1.4 +PYTHONVER=2.7 + +KLISH_SRC = $(SONIC_CLI_ROOT)/klish-$(KLISH_VERSION) + +SRC_REPLACEMENTS:=$(shell find patches -type f) +all : $(SRC_REPLACEMENTS) + tar xzvf klish-$(KLISH_VERSION).tgz -C $(SONIC_CLI_ROOT) + ./patches/scripts/patchmake.sh -p VER=${KLISH_VERSION} TSP=${SONIC_CLI_ROOT} DSP=${CURDIR}/patches TWP=${SONIC_CLI_ROOT} + + cd ${KLISH_SRC} && sh autogen.sh && ./configure --with-libxml2=/usr && make + + mkdir -p $(SONIC_CLI_ROOT)/target/.libs + cp $(CURDIR)/clish_start $(SONIC_CLI_ROOT)/target/. + + cp -r ${KLISH_SRC}/bin/.libs/clish ${SONIC_CLI_ROOT}/target/. + cp -r ${KLISH_SRC}/.libs/*.so* ${SONIC_CLI_ROOT}/target/.libs + cp -r ${KLISH_SRC}/.libs/*.a ${SONIC_CLI_ROOT}/target/.libs + @echo "complete klish build" + +.PHONY: clean +clean: + rm -rf ${KLISH_SRC}/klish-$(KLISH_VERSION) diff --git a/src/CLI/klish/clish_start b/src/CLI/klish/clish_start new file mode 100755 index 0000000000..410b85345e --- /dev/null +++ b/src/CLI/klish/clish_start @@ -0,0 +1,14 @@ +#!/bin/bash + +export SONIC_MGMT_ROOT=/usr/sbin +export SONIC_CLI_ROOT=$SONIC_MGMT_ROOT/cli +if [ -z $SYSTEM_NAME ] +then + export SYSTEM_NAME="${HOSTNAME%%.*}" +fi +export PYTHONPATH=$SONIC_MGMT_ROOT:$SONIC_MGMT_ROOT/lib/swagger_client_py:$SONIC_CLI_ROOT:$SONIC_CLI_ROOT/scripts:$PYTHONPATH +export CLISH_PATH=$SONIC_CLI_ROOT/command-tree +export LD_LIBRARY_PATH=/usr/local/lib:$SONIC_CLI_ROOT/.libs:$LD_LIBRARY_PATH +export PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin:$SONIC_CLI_ROOT + +$SONIC_CLI_ROOT/clish "$@" diff --git a/src/CLI/klish/klish-2.1.4.tgz b/src/CLI/klish/klish-2.1.4.tgz new file mode 100644 index 0000000000..e83e2f8188 Binary files /dev/null and b/src/CLI/klish/klish-2.1.4.tgz differ diff --git a/src/CLI/klish/patches/klish-2.1.4/clish/shell/shell_libxml2.c.diff b/src/CLI/klish/patches/klish-2.1.4/clish/shell/shell_libxml2.c.diff new file mode 100644 index 0000000000..c9bb5cc847 --- /dev/null +++ b/src/CLI/klish/patches/klish-2.1.4/clish/shell/shell_libxml2.c.diff @@ -0,0 +1,102 @@ +diff --git a/clish/shell/shell_libxml2.c b/clish/shell/shell_libxml2.c +index 7acca05..0d51607 100644 +--- a/clish/shell/shell_libxml2.c ++++ b/clish/shell/shell_libxml2.c +@@ -2,7 +2,7 @@ + * ------------------------------------------------------ + * shell_roxml.c + * +- * This file implements the means to read an XML encoded file ++ * This file implements the means to read an XML encoded file + * and populate the CLI tree based on the contents. It implements + * the clish_xml API using roxml + * ------------------------------------------------------ +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + #include "xmlapi.h" + + #ifdef HAVE_LIB_LIBXSLT +@@ -69,7 +70,7 @@ static inline clish_xmlnode_t *node_to_xmlnode(xmlNode *node) + int clish_xmldoc_start(void) + { + #ifdef HAVE_LIB_LIBXSLT +- /* The XSLT example contain these settings but I doubt ++ /* The XSLT example contain these settings but I doubt + * it's really necessary. + */ + /* xmlSubstituteEntitiesDefault(1); +@@ -91,7 +92,8 @@ int clish_xmldoc_stop(void) + clish_xmldoc_t *clish_xmldoc_read(const char *filename) + { + xmlDoc *doc; +- doc = xmlReadFile(filename, NULL, 0); ++ doc = xmlReadFile(filename, NULL, 1026); ++ xmlXIncludeProcess(doc); + return doc_to_xmldoc(doc); + } + +@@ -131,15 +133,15 @@ int clish_xmlnode_get_type(clish_xmlnode_t *node) + if (node) { + xmlNode *n = xmlnode_to_node(node); + switch (n->type) { +- case XML_ELEMENT_NODE: ++ case XML_ELEMENT_NODE: + return CLISH_XMLNODE_ELM; +- case XML_TEXT_NODE: ++ case XML_TEXT_NODE: + return CLISH_XMLNODE_TEXT; +- case XML_COMMENT_NODE: ++ case XML_COMMENT_NODE: + return CLISH_XMLNODE_COMMENT; +- case XML_PI_NODE: ++ case XML_PI_NODE: + return CLISH_XMLNODE_PI; +- case XML_ATTRIBUTE_NODE: ++ case XML_ATTRIBUTE_NODE: + return CLISH_XMLNODE_ATTR; + default: + break; +@@ -169,7 +171,7 @@ clish_xmlnode_t *clish_xmlnode_parent(clish_xmlnode_t *node) + return NULL; + } + +-clish_xmlnode_t *clish_xmlnode_next_child(clish_xmlnode_t *parent, ++clish_xmlnode_t *clish_xmlnode_next_child(clish_xmlnode_t *parent, + clish_xmlnode_t *curchild) + { + xmlNode *child; +@@ -208,11 +210,11 @@ char *clish_xmlnode_fetch_attr(clish_xmlnode_t *node, + a = a->next; + } + } +- ++ + return NULL; + } + +-int clish_xmlnode_get_content(clish_xmlnode_t *node, char *content, ++int clish_xmlnode_get_content(clish_xmlnode_t *node, char *content, + unsigned int *contentlen) + { + xmlNode *n; +@@ -258,7 +260,7 @@ int clish_xmlnode_get_content(clish_xmlnode_t *node, char *content, + } + } + +-int clish_xmlnode_get_name(clish_xmlnode_t *node, char *name, ++int clish_xmlnode_get_name(clish_xmlnode_t *node, char *name, + unsigned int *namelen) + { + int rlen; +@@ -276,7 +278,7 @@ int clish_xmlnode_get_name(clish_xmlnode_t *node, char *name, + *name = 0; + n = xmlnode_to_node(node); + rlen = strlen((char*)n->name) + 1; +- ++ + if (rlen <= *namelen) { + snprintf(name, *namelen, "%s", (char*)n->name); + name[*namelen - 1] = '\0'; diff --git a/src/CLI/klish/patches/scripts/patchmake.sh b/src/CLI/klish/patches/scripts/patchmake.sh new file mode 100755 index 0000000000..1dddfcaa06 --- /dev/null +++ b/src/CLI/klish/patches/scripts/patchmake.sh @@ -0,0 +1,190 @@ +#!/bin/bash +# +# This script walks through all files in a directory and patches / copies them to the +# requested destination directory. +# If the file name has .diff suffix, it is patched. Otherwise the file is copied + +CLEAN_ALL="no" +CLEAN="no" +PATCH="no" +SKIP_PATCH="no" +MAKE_CLEAN="no" +MAKE_SKIP="no" + +TMP_VAR="" + +echo "## Executing `pwd`/$0" + +pre_exec(){ + if [ -z "$CODE_VER" ]; then + echo "## Specify the klish version in x.x.x format" + exit -1 + fi + TMP_SRC_PATH2="./klish-$CODE_VER" + TMP_SRC_PATH2="./klish-$CODE_VER" + +} + + +while [[ $# -gt 0 ]] +do + opt="$1" + shift + + case $opt in + #Removes the temporary directory and start the process of sync, patch and make. + -c|--clean) + CLEAN="yes" + ;; + + #Removes the temporary directory only and exits. All other options are ignored. + -C|--clean-all) + CLEAN_ALL="yes" + ;; + + #Displays the help for the patchmake.sh script. + -h|--help) + echo -ne "\rVariables:\n" + echo -ne "\rVER - Set the code version\n" + echo -ne "\rDSP - Set the .diff files path\n" + echo -ne "\rTSP - Set the source path to which the code need to extracted\n" + echo -ne "\rTWP - Set the code work path where the source will be copied and patched with .diff files\n" + echo -ne "\r\nOptions:\n" + echo -ne "\r-c, --clean\n\tRemoves the temporary directory for current version and start the process of sync, patch and make.\n\n" + echo -ne "\r-C, --clean-all\n\tRemoves the temporary directory of all version and exits. All other options are ignored.\n\n" + echo -ne "\r-h, --help\n\tDisplays the help for the make.sh script.\n\n" + echo -ne "\r-m, --make-clean\n\tDoes the make for \"clean\" target before building the actual target.\n\n" + echo -ne "\r-M, --skip-make\n\tThe make for the actual target is skipped. Ignored when used along with option -P --skip-patch\n\n." + echo -ne "\r-p, --patch-only\n\tThe script exits after patching the .diff files. Ignored when used along with option -P, --skip-patch.\n\n" + echo -ne "\r-P, --skip-patch\n\tThe patching of the .diff files is alone skipped. Used when required to build the target without any patches.\n\n" + exit 0 + ;; + + #Does the make for "clean" target before building the actual target. + -m|--make-clean) + MAKE_CLEAN="yes" + ;; + + #The make for the actual target is skipped. + -M|--skip-make) + MAKE_SKIP="yes" + ;; + + #The script exits after patching the .diff files. + -p|--patch-only) + PATCH="yes" + ;; + + #The patching of the .diff files is alone skipped. + -P|--skip-patch) + SKIP_PATCH="yes" + ;; + + #The source version to be compiled + VER=[0-9].[0-9].[0-9]) + CODE_VER=`echo $opt | awk -F'=' '{print $2}'` + ;; + + #Temporary source path + TSP=*) + TMP_SRC_PATH=`echo $opt | awk -F'=' '{print $2}'` + ;; + + #Temporary work path + TWP=*) + TMP_PATH=`echo $opt | awk -F'=' '{print $2}'` + ;; + + #Diff files path + DSP=*) + DIFF_SRC_PATH=`echo $opt | awk -F'=' '{print $2}'` + ;; + + #Unknown input + *) + echo "Unknown option or input $opt" + exit -1 + ;; + esac +done + +if [ "$TMP_SRC_PATH" == "" ]; then + echo "Temporary source path 'TSP' not set" + exit -1 +fi +if [ "$TMP_PATH" == "" ]; then + echo "Temporary work path 'TWP' not set" + TWP=${TSP} +fi +if [ "$DIFF_SRC_PATH" == "" ]; then + echo "Diff file(s) path 'DSP' not set" + exit -1 +fi +if [ "$CODE_VER" == "" ]; then + echo "Code version not set" + exit -1 +fi + +#Handling of clean only +if [ "$CLEAN_ALL" == "yes" ]; then + echo "## Removing $TMP_PATH directory" + rm -rf $TMP_PATH + exit 0 +fi + +pre_exec + +#Handling of clean option +if [ "$CLEAN" == "yes" ]; then + echo "## Cleaning the existing files in $TMP_PATH/$TMP_SRC_PATH2" + rm -rf $TMP_PATH/$TMP_SRC_PATH2 +fi + +mkdir -p $TMP_PATH + +#Handling of skipping patch +if [ ! "$SKIP_PATCH" == "yes" ]; then + if [ ! -f "$TMP_PATH/$TMP_SRC_PATH2/##patched##" ]; then + + #Copying the actual source files into the temporary directory + cp -r $TMP_SRC_PATH/$TMP_SRC_PATH2 $TMP_PATH + + #Getting the list of files + echo "## Preparing the .diff file list..." + TMP_VAR=`pwd` + cd $DIFF_SRC_PATH/$TMP_SRC_PATH2 + files=`find . -type f` + cd $TMP_VAR + + #Applying the patch or copying the newly created files + echo "## Applying the patch from $DIFF_SRC_PATH/$TMP_SRC_PATH2" | tee "$TMP_PATH/$TMP_SRC_PATH2/##patchlog##" + for file in $files + do + #Copying the files directly into the temporary source directory if is not a .diff file + TMP_VAR=`dirname $file` + if [ "${file##*.}" != "diff" ]; then + #Creating new directories if doesn't exist already and then copying the files + echo "copying $DIFF_SRC_PATH/$TMP_SRC_PATH2/$file $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR" | tee -a "$TMP_PATH/$TMP_SRC_PATH2/##patchlog##" + test -d "$TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR" || mkdir -p "$TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR" && cp $DIFF_SRC_PATH/$TMP_SRC_PATH2/$file $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR + fi + + #Patching the .diff files + TMP_VAR="${file%.*}" + if [ -f "$TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR" ]; then + patch $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR $DIFF_SRC_PATH/$TMP_SRC_PATH2/$file -o $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR.tmp | tee -a "$TMP_PATH/$TMP_SRC_PATH2/##patchlog##" + echo "moving $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR.tmp -> $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR" | tee -a "$TMP_PATH/$TMP_SRC_PATH2/##patchlog##" + cp $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR.tmp $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR + rm $TMP_PATH/$TMP_SRC_PATH2/$TMP_VAR.tmp + fi + done + touch "$TMP_PATH/$TMP_SRC_PATH2/##patched##" + else + echo "## Patching diff files is skipped -- already patched" + fi + if [ "$PATCH" == "yes" ]; then + exit + fi +else + echo "## Patching diff files is skipped -- user input" +fi + diff --git a/src/CLI/renderer/Makefile b/src/CLI/renderer/Makefile new file mode 100644 index 0000000000..7da49f4995 --- /dev/null +++ b/src/CLI/renderer/Makefile @@ -0,0 +1,6 @@ +.PHONY: install +all: + @echo "nothing to install for now" + +clean: + @echo "nothing to clean for now" diff --git a/src/CLI/renderer/scripts/__init__.py b/src/CLI/renderer/scripts/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/CLI/renderer/scripts/acl_jinja2.py b/src/CLI/renderer/scripts/acl_jinja2.py new file mode 100755 index 0000000000..6f7b20f35c --- /dev/null +++ b/src/CLI/renderer/scripts/acl_jinja2.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +from jinja2 import Template +import os +acl_out = {'openconfig_aclacl': {'acl_sets': {'acl_set': [{'acl_entries': {'acl_entry': [{'actions': {'config': {'forwarding_action': 'DROP', + 'log_action': None}, + 'state': {'forwarding_action': 'DROP', + 'log_action': None}}, + 'config': {'description': None, + 'sequence_id': 66}, + 'input_interface': None, + 'ipv4': {'config': {'destination_address': '2.2.2.0/24', + 'dscp': None, + 'hop_limit': None, + 'protocol': '6', + 'source_address': '1.1.1.0/24'}, + 'state': {'destination_address': '2.2.2.0/24', + 'dscp': None, + 'hop_limit': None, + 'protocol': '6', + 'source_address': '1.1.1.0/24'}}, + 'ipv6': None, + 'l2': None, + 'sequence_id': 66, + 'state': {'description': None, + 'matched_octets': 0, + 'matched_packets': 0, + 'sequence_id': 66}, + 'transport': {'config': {'destination_port': '200', + 'source_port': '100', + 'tcp_flags': None}, + 'state': {'destination_port': '200', + 'source_port': '100', + 'tcp_flags': None}}}]}, + 'config': {'description': None, + 'name': 'TEST', + 'type': 'ACL_IPV4'}, + 'name': 'TEST', + 'state': {'description': None, + 'name': 'TEST', + 'type': 'ACL_IPV4'}, + 'type': 'ACL_IPV4'}]}, + 'interfaces': None, + 'state': None}} + + + + +#!/usr/bin/env/python + +from jinja2 import Environment, FileSystemLoader + +# Capture our current directory +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + +def acl_show(): + # Create the jinja2 environment. + # Notice the use of trim_blocks, which greatly helps control whitespace. + j2_env = Environment(loader=FileSystemLoader(THIS_DIR), + trim_blocks=True) + print (j2_env.get_template('acl_show.j2').render(acl_out=acl_out)) + +if __name__ == '__main__': + acl_show() diff --git a/src/CLI/renderer/scripts/render_cli.py b/src/CLI/renderer/scripts/render_cli.py new file mode 100755 index 0000000000..b2bc829904 --- /dev/null +++ b/src/CLI/renderer/scripts/render_cli.py @@ -0,0 +1,133 @@ +#!/usr/bin/env python +from jinja2 import Template, Environment, FileSystemLoader +import os +import json +import sys +import gc +import select +from rpipe_utils import pipestr + + +# Capture our current directory +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + +global line_count +global ctrl_rfd + +def render_init(fd): + global ctrlc_rfd + + ctrlc_rd_fd_num = int(fd) + try: + ctrlc_rfd = os.fdopen(ctrlc_rd_fd_num, 'r') + except IOError as e: + sys.stdout.write("Received error : " + str(e)) + gc.collect() + return None + +def cli_getch(): + # Disable canonical mode of input stream + # Set min bytes as 1 and read operation as blocking + fd = sys.stdin.fileno() + c = None + + #global ctrc_rfd + #fds = [fd, ctrlc_rfd] + fds = [fd] + try: + read_fds, write_fds, excep_fds = select.select(fds, [], []) + """ + # Return immediately for Ctrl-C interrupt + if ctrlc_rfd in read_fds: + return 'q' + """ + + c = os.read(fd, 1) + except KeyboardInterrupt: + return 'q' + except select.error as e: + if e[0] == 4: # Interrupted system call + return 'q' + else: + sys.stdout.write("Received error : " + str(e)) + return c + +def _write(string, disable_page=False): + """ + This function would take care of complete pagination logic, + like printing --more--, accepting SPACE, ENTER, q, CTRL-C + and act accordingly + """ + global line_count + + page_len_local = 25 + terminal = sys.stdout + # set length as 0 for prints without pagination + if disable_page is True: + page_len_local = 0 + if len(string) != 0: + terminal.write(string + '\n') + if page_len_local == 0: + return False + line_count = line_count + 1 + if line_count == page_len_local: + terminal.write("--more--") + while 1: + terminal.flush() + c = cli_getch() + terminal.flush() + # End of text (ascii value 3) is returned while pressing Ctrl-C + # key when CLISH executes commands from non-TTY + # Example : clish_source plugin + if c == 'q' or ord(c) == 3: + terminal.write('\x1b[2K'+'\x1b[0G') + line_count = 0 + #self.is_stopped = True + return True + elif c == ' ': + line_count = 0 + terminal.write('\x1b[2K'+'\x1b[0G') + break + # Carriage return (\r) is returned while pressing ENTER + # key when CLISH executes commands from non-TTY + # Example : clish_source plugin + elif c == '\n' or c == '\r': + #line_count = page_len_local - 1 + line_count = 0 + terminal.write('\x1b[2K'+'\x1b[0G') + terminal.flush() + break + return False + +def write(t_str): + global line_count + line_count = 0 + q = False + + render_init(0) + if t_str != "": + pipelst = pipestr().read(); + for s_str in t_str.split('\n'): + if pipelst: + if pipelst.process_pipes(s_str): + q = _write(s_str, pipelst.is_page_disabled()) + else: + q = _write(s_str) + if q: + break + + +def show_cli_output(template_file, response): + # Create the jinja2 environment. + # Notice the use of trim_blocks, which greatly helps control whitespace. + + template_path = os.path.abspath(os.path.join(THIS_DIR, "../render-templates")) + + j2_env = Environment(loader=FileSystemLoader(template_path),extensions=['jinja2.ext.do']) + j2_env.trim_blocks = True + j2_env.lstrip_blocks = True + j2_env.rstrip_blocks = True + + if response: + t_str = (j2_env.get_template(template_file).render(json_output=response)) + write(t_str) diff --git a/src/CLI/renderer/scripts/rpipe_utils.py b/src/CLI/renderer/scripts/rpipe_utils.py new file mode 100644 index 0000000000..d2377c536f --- /dev/null +++ b/src/CLI/renderer/scripts/rpipe_utils.py @@ -0,0 +1,382 @@ +#!/usr/bin/env python + +import re +import os +from time import gmtime, strftime + +class pipestr: + """ + pipestr class + For passing the pipestr from the actioner to the renderer + """ + def __init__(self): + pass + + def write(self, argv): + pipe_str = '' + has_pipe = False + for arg in argv: + if has_pipe: + pipe_str += (arg + ' ') + if arg == '|': + has_pipe = True + f = open("pipestr.txt", "w") + if len(pipe_str) > 0: + pipe_str = pipe_str[:-1] + f.write(pipe_str) + f.close() + + def read(self): + pipe_lst = pipelst() + f = open('pipestr.txt', "r") + pipe_str = f.readline() + f.close() + if len(pipe_str) > 0: + if pipe_lst.build_pipes(pipe_str) != 0: + print("error bulding pipe") + return None + return pipe_lst + +class pipelst: + """ + pipelst class + """ + def __init__(self): + # List of pipe objects corresponds to pipe string + self.pipes = [] + self.disable_page = False + + ## + # @brief Preprocess the pipe string and build the pipe objects list + # for later use + # + # @param pipe_str The string following the '|' symbol in the command line + # + # @return None + def build_pipes(self, pipe_str): + """validate pipe string and build pipe objects""" + splitlist = [] + pipe_obj = None + + if pipe_str is None: + return 0 + + # 'save skip-header' is internally triggered for 'show diff' + #if not pipe_str.startswith("save ") and show_batch_obj_g.is_obj_set(): + # if -1 == show_batch_obj_g.pipe_action(pipe_str): + # return -1 + # pipe_str = pipe_str + show_batch_obj_g.get_pipe_str() + + # Check for 'no-more' and disble pagination + if "no-more" in pipe_str: + self.disable_page = True + + splitlist = [x.strip() for x in pipe_str.split(" | ")] + for cmd in splitlist: + tmplist = cmd.split(' ', 1) + if len(tmplist) > 1: + match_str = tmplist[1].lstrip() + match_str = re.sub(r'^"|"$', '', match_str) + else: + continue + + pipe_cmd = tmplist[0].lower() + if pipe_cmd == "grep": + try : + pipe_obj = rpipe_grep(match_str) + except : + return -1 + elif pipe_cmd == "except": + try : + pipe_obj = rpipe_except(match_str) + except : + return -1 + elif pipe_cmd == "find": + try : + pipe_obj = rpipe_find(match_str) + except : + return -1 + elif pipe_cmd == "save": + # Check additional options + skip_header = False + write_mode = 'w' + file_name = match_str + if ' ' in match_str: + match_str_parts = match_str.split(' ') + file_name = match_str_parts[0] + save_option = match_str_parts[1].lower() + # skip-header is used for 'show diff' + if save_option == "skip-header": + skip_header = True + elif save_option == "append": + write_mode = 'a' + try : + pipe_obj = rpipe_save(file_name, write_mode, pipe_str, skip_header) + except : + return -1 + else: + pass + + if pipe_obj != None : + self.pipes.append(pipe_obj) + + return 0 + + ## + # @brief process the pipe objects list against the string + # + # @param string to be processed + # + # @return True/False + def process_pipes(self, string): + """process pipe objects against the string""" + pipe_result = False + print_content = True + + pipe_list = list(self.pipes) + for pipeobj in pipe_list: + pipe_result = pipeobj.pipe_action(string) + if pipe_result == False: + print_content = False + break + # Remove the pipe if needed (for find) + if pipeobj.can_be_removed() == True: + self.pipes.remove(pipeobj) + # Get the status whether can be printed or not + # For save, console print is not necessary + print_content = pipeobj.can_be_printed() + + return print_content + + ## + # @brief destroy the pipe objects + # + # @return None + def destroy_pipes(self): + """destroys pipe objects""" + self.pipes = [] + # enable pagination + self.disable_page = False + return + + ## + # @brief print the pipe objects + # + # @return None + def print_pipes(self): + """dump pipe objects""" + for pipeobj in self.pipes: + print pipeobj + return + + def is_page_disabled(self): + """returns the status of pagination enabled/disabled""" + return self.disable_page + + def __del__(self): + self.destroy_pipes() + +class rpipe_grep: + """ + grep wrapper class + """ + def __init__(self, match_str): + self.remove_pipe = False + self.print_content = True + self.pipe_str = "grep " + match_str + flags = None + if match_str.endswith(" ignore-case"): + flags = re.I + match_str = match_str.rsplit(' ', 1)[0] + try : + if flags is None: + self.regexp = re.compile(r'(.*?)' + match_str + '(.*?)') + else: + self.regexp = re.compile(r'(.*?)' + match_str + '(.*?)', flags) + except Exception, error : + print '%Error: ' + str(error) + raise + + def pipe_action(self, string): + if self.regexp.search(string) != None: + return True + else: + return False + + def can_be_removed(self): + return self.remove_pipe + + def can_be_printed(self): + return self.print_content + + def get_pipe_str(self): + return self.pipe_str + + def __del__(self): + self.regexp = "" + +class rpipe_except: + """ + except wrapper class + """ + def __init__(self, match_str): + self.remove_pipe = False + self.print_content = True + self.pipe_str = "except " + match_str + flags = None + if match_str.endswith(" ignore-case"): + flags = re.I + match_str = match_str.rsplit(' ', 1)[0] + try : + if flags is None: + self.regexp = re.compile(r'(.*?)' + match_str + '(.*?)') + else: + self.regexp = re.compile(r'(.*?)' + match_str + '(.*?)', flags) + except Exception, error : + print '%Error: ' + str(error) + raise + + def pipe_action(self, string): + if self.regexp.search(string) == None: + return True + else: + return False + + def can_be_removed(self): + return self.remove_pipe + + def can_be_printed(self): + return self.print_content + + def get_pipe_str(self): + return self.pipe_str + + def __del__(self): + self.regexp = "" + +class rpipe_find: + """ + find wrapper class + """ + def __init__(self, match_str): + self.remove_pipe = True + self.print_content = True + self.pipe_str = "find " + match_str + flags = None + if match_str.endswith(" ignore-case"): + flags = re.I + match_str = match_str.rsplit(' ', 1)[0] + try : + if flags is None: + self.regexp = re.compile(r'(.*?)' + match_str + '(.*?)') + else: + self.regexp = re.compile(r'(.*?)' + match_str + '(.*?)', flags) + except Exception, error : + print '%Error: ' + str(error) + raise + + def pipe_action(self, string): + if self.regexp.search(string) != None: + return True + else: + return False + + def can_be_removed(self): + return self.remove_pipe + + def can_be_printed(self): + return self.print_content + + def get_pipe_str(self): + return self.pipe_str + + def __del__(self): + self.regexp = "" + +class rpipe_save: + """ + save wrapper class + """ + def __init__(self, file_path, file_mode, cmd_str, skip_header=False): + self.remove_pipe = False + self.print_content = False + self.pipe_str = "save " + file_path + self.fd = None + if os.path.isabs(file_path) is True: + if os.path.exists(file_path) is True: + file_dir = os.path.dirname(file_path) + file_name = os.path.basename(file_path) + if file_name != "": + try: + self.fd = open(file_path, file_mode) + except IOError: + print 'Error: cannot create regular file ', \ + '%s : No such file or Directory' % file_path + else: + print "File name not present in %s" % file_path + else: + file_dir = os.path.dirname(file_path) + file_name = os.path.basename(file_path) + if os.path.isdir(file_dir) is True: + try: + self.fd = open(file_path, file_mode) + except IOError: + print 'Error: cannot create regular file ', \ + '%s : No such file or Directory' % file_path + else: + print '%s is not a Valid filepath' % file_path + else: + # For relative path, store the result in user's home + file_path = os.path.expanduser('~') + '/' + file_path + try: + self.fd = open(file_path, file_mode) + except IOError: + print 'Error: cannot create regular file ', \ + '%s : No such file or Directory' % file_path + # Save computed file name for future reference + self.file_path = file_path + # Write header in file + if skip_header is False: + self.write_header(cmd_str) + + def pipe_action(self, string): + # Print error when fd is not valid due to some reasons + if self.fd == None: + return False + + try: + if len(string) != 0: + self.fd.write(string + '\n') + self.fd.flush() + except IOError: + print 'Error: Write into file %s failed' % self.file_path + self.fd.close() + return True + + def can_be_removed(self): + return self.remove_pipe + + def can_be_printed(self): + return self.print_content + + def get_pipe_str(self): + return self.pipe_str + + def write_header(self, cmd_str): + if self.fd != None: + try: + self.fd.write('\n' + "! ===================================" + + "=====================================" + '\n' + + "! Started saving show command output at " + + strftime("%d/%m, %Y, %H:%M:%S", gmtime()) + + " for command:" + '\n! ' + cmd_str + '\n' + + "! ===================================" + + "=====================================" + '\n') + except IOError: + print 'Error: Write into file %s failed' % self.file_path + self.fd.close() + + def __del__(self): + if self.fd != None: + self.fd.close() + diff --git a/src/CLI/renderer/show_collector.j2 b/src/CLI/renderer/show_collector.j2 new file mode 100755 index 0000000000..73686332e6 --- /dev/null +++ b/src/CLI/renderer/show_collector.j2 @@ -0,0 +1,31 @@ +{% set vars = {'collectorName': ""} %} +{% set vars = {'ipType': ""} %} +{% set vars = {'ipAddress': ""} %} +{% set vars = {'portNum': ""} %} +{{'---------------------------------------------------------------------------'}} +{{'NAME'.ljust(20)}} {{'IP TYPE'.ljust(20)}} {{'IP'.ljust(20)}} {{'PORT'}} +{{'----------------------------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for item in value %} +{% for key2,value2 in item.items() %} +{% if 'coll-key' == key2 %} +{% if vars.update({'collectorName':value2}) %}{% endif %} +{% endif %} +{% if 'each-coll-data' == key2 %} +{% for key,value in value2.items() %} +{% if 'ipaddress-type' == key %} +{% if vars.update({'ipType':value}) %}{% endif %} +{% endif %} +{% if 'ipaddress' == key %} +{% if vars.update({'ipAddress':value}) %}{% endif %} +{% endif %} +{% if 'port' == key %} +{% if vars.update({'portNum':value}) %}{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{{'%-22s'|format(vars.collectorName)}}{{'%-18s'|format(vars.ipType)}} {{'%-20s'|format(vars.ipAddress)}} {{'%-4s'|format(vars.portNum)}} +{% endfor %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/acl_show.j2 b/src/CLI/renderer/templates/acl_show.j2 new file mode 100644 index 0000000000..3a70dedef9 --- /dev/null +++ b/src/CLI/renderer/templates/acl_show.j2 @@ -0,0 +1,4 @@ +{% set acl_sets = acl_out['openconfig_aclacl']['acl_sets']['acl_set'] %} + {% for acl_set in acl_sets %} + Name: {{ acl_set['state']['description'] }} + {% endfor %} diff --git a/src/CLI/renderer/templates/lldp_neighbor_show.j2 b/src/CLI/renderer/templates/lldp_neighbor_show.j2 new file mode 100755 index 0000000000..16c5134783 --- /dev/null +++ b/src/CLI/renderer/templates/lldp_neighbor_show.j2 @@ -0,0 +1,29 @@ +{{'-----------------------------------------------------------'}} +{{'LLDP Neighbors'.ljust(20)}} +{{'-----------------------------------------------------------'}} +{% for neigh in json_output %} +{% set value = neigh['neighbors']['neighbor'][0] %} +{{'Interface:'}}{{' '}}{{value['id']}}{{',via:'}}{{' LLDP'}} +{{' Chassis:'}} +{{' ChassisID: '}}{{value['state']['chassis_id']}} +{{' SysName: '}}{{value['state']['system_name']}} +{% set desc = value['state']['system_description'].split('\r\n')[1:] %} +{{' SysDescr: '}}{{value['state']['system_description'].split('\r\n')[0]}} +{% for v in desc %} +{{' '}}{{v}} +{% endfor %} +{% for cap in value['capabilities']['capability'] %} +{% if cap['state']['enabled'] == true %} +{% set en = 'ON' %} +{% endif %} +{% if cap['state']['enabled'] == false %} +{% set en = 'OFF' %} +{% endif %} +{{' Capability: '}}{{cap['name'].split(':')[1]}}{{', '}}{{en}} +{% endfor %} +{{' Port'}} +{{' PortID: '}}{{value['state']['port_id']}} +{{' PortDescr: '}}{{value['state']['port_description']}} +{{'-----------------------------------------------------------'}} +{% endfor %} + diff --git a/src/CLI/renderer/templates/lldp_show.j2 b/src/CLI/renderer/templates/lldp_show.j2 new file mode 100644 index 0000000000..1f8eaf71d5 --- /dev/null +++ b/src/CLI/renderer/templates/lldp_show.j2 @@ -0,0 +1,18 @@ +{{'------------------------------------------------------------------------------------------------------'}} +{{'LocalPort'.ljust(20)}}{{'RemoteDevice'.ljust(20)}}{{'RemotePortID'.ljust(20)}}{{'Capability'.ljust(20)}} {{'RemotePortDescr'}} +{{'-------------------------------------------------------------------------------------------------------'}} +{% set cap_dict = {'openconfig-lldp-types:REPEATER' : 'O','openconfig-lldp-types:MAC_BRIDGE' : 'B' , 'openconfig-lldp-types:ROUTER' : 'R'} %} +{% for neigh in json_output %} +{% set capabilities = neigh['neighbors']['neighbor'][0]['capabilities']['capability'] %} +{% set cap_list = [] %} +{% for cap in capabilities %} +{% if cap['state']['name'] in cap_dict %} +{% if cap['state']['enabled'] == true %} +{% do cap_list.append(cap_dict[cap['state']['name']]) %} +{% endif %} +{% endif %} +{% endfor %} +{% set value = neigh['neighbors']['neighbor'][0] %} +{{value['id'].ljust(20)}}{{value['state']['system_name'].ljust(20)}}{{value['state']['port_id'].ljust(20)}}{{(cap_list | join() | string).ljust(20)}}{{value['state']['port_description'].ljust(20)}} +{% endfor %} + diff --git a/src/CLI/renderer/templates/platform_show.j2 b/src/CLI/renderer/templates/platform_show.j2 new file mode 100644 index 0000000000..cd18d406ad --- /dev/null +++ b/src/CLI/renderer/templates/platform_show.j2 @@ -0,0 +1,7 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(20)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{{key.ljust(20)}}:{{value}} +{% endfor %} + diff --git a/src/CLI/renderer/templates/ptp_clock_show.j2 b/src/CLI/renderer/templates/ptp_clock_show.j2 new file mode 100755 index 0000000000..2371fee3bd --- /dev/null +++ b/src/CLI/renderer/templates/ptp_clock_show.j2 @@ -0,0 +1,35 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(21)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% if 'domain-number' in value.keys() %} +{{ 'Domain Number'.ljust(21)}} {{ value['domain-number']}} +{% endif %} +{% if 'priority1' in value.keys() %} +{{ 'Priority1'.ljust(21)}} {{ value['priority1']}} +{% endif %} +{% if 'priority2' in value.keys() %} +{{ 'Priority2'.ljust(21)}} {{ value['priority2']}} +{% endif %} +{% if 'two-step-flag' in value.keys() %} +{{ 'Two Step'.ljust(21)}} {{ value['two-step-flag']}} +{% endif %} +{% if 'slave-only' in value.keys() %} +{{ 'Slave Only'.ljust(21)}} {{ value['slave-only']}} +{% endif %} +{% if 'number-ports' in value.keys() %} +{{ 'Number Ports'.ljust(21)}} {{ value['number-ports']}} +{% endif %} +{% if 'clock-quality' in value.keys() %} +{{ 'Clock Quality:'.ljust(21)}} +{% if 'clock-class' in value['clock-quality'].keys() %} +{{ ' Clock Class'.ljust(21)}} {{ value['clock-quality']['clock-class']}} +{% endif %} +{% if 'clock-accuracy' in value['clock-quality'].keys() %} +{{ ' Clock Accuracy'.ljust(21)}} {{ value['clock-quality']['clock-accuracy']}} +{% endif %} +{% if 'offset-scaled-log-variance' in value['clock-quality'].keys() %} +{{ ' Ofst Scaled Log Var'.ljust(21)}} {{ value['clock-quality']['offset-scaled-log-variance']}} +{% endif %} +{% endif %} +{% endfor %} diff --git a/src/CLI/renderer/templates/ptp_parent_show.j2 b/src/CLI/renderer/templates/ptp_parent_show.j2 new file mode 100755 index 0000000000..e3af061473 --- /dev/null +++ b/src/CLI/renderer/templates/ptp_parent_show.j2 @@ -0,0 +1,46 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(30)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% if 'parent-port-identity' in value.keys() %} +{% for key2,value2 in value['parent-port-identity'].items() %} +{% if 'clock-identity' == key2 %} +{{ 'Parent Clock Identity'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'port-number' == key2 %} +{{ 'Port Number'.ljust(30)}} {{ value2 }} +{% endif %} +{% endfor %} +{% endif %} +{% if 'grandmaster-clock-quality' in value.keys() %} +{% for key2,value2 in value['grandmaster-clock-quality'].items() %} +{% if 'clock-class' == key2 %} +{{ 'Grandmaster Clock Class'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'clock-accuracy' == key2 %} +{{ 'Grandmaster Clock Accuracy'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'offset-scaled-log-variance' == key2 %} +{{ 'Grandmaster Off Scaled Log Var'.ljust(30)}} {{ value2 }} +{% endif %} +{% endfor %} +{% endif %} +{% if 'grandmaster-identity' in value.keys() %} +{{ 'Grandmaster Identity'.ljust(30)}} {{ value['grandmaster-identity']}} +{% endif %} +{% if 'grandmaster-priority1' in value.keys() %} +{{ 'Grandmaster Priority1'.ljust(30)}} {{ value['grandmaster-priority1']}} +{% endif %} +{% if 'grandmaster-priority2' in value.keys() %} +{{ 'Grandmaster Priority2'.ljust(30)}} {{ value['grandmaster-priority2']}} +{% endif %} +{% if 'parent-stats' in value.keys() %} +{{ 'Stats Valid'.ljust(30)}} {{ value['parent-stats']}} +{% endif %} +{% if 'observed-parent-offset-scaled-log-variance' in value.keys() %} +{{ 'Observed Off Scaled Log Var'.ljust(30)}} {{ value['observed-parent-offset-scaled-log-variance']}} +{% endif %} +{% if 'observed-parent-clock-phase-change-rate' in value.keys() %} +{{ 'Observed Clock Phase Chg Rate'.ljust(30)}} {{ value['observed-parent-clock-phase-change-rate']}} +{% endif %} +{% endfor %} diff --git a/src/CLI/renderer/templates/ptp_port_show.j2 b/src/CLI/renderer/templates/ptp_port_show.j2 new file mode 100755 index 0000000000..42c4b29214 --- /dev/null +++ b/src/CLI/renderer/templates/ptp_port_show.j2 @@ -0,0 +1,37 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(30)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for item in value %} +{% for key2,value2 in item.items() %} +{% if 'port-number' == key2 %} +{{ 'Port Number'.ljust(30)}} {{ value2}} +{% endif %} +{% if 'port-state' == key2 %} +{{ 'Port State'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'log-min-delay-req-interval' == key2.keys %} +{{ 'Log Min delay Req Intvl'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'peer-mean-path-delay' == key2 %} +{{ 'Peer Mean Path delay'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'log-announce-interval' == key2 %} +{{ 'Log Announce Interval'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'log-sync-interval' == key2 %} +{{ 'Log Sync Interval'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'delay-mechanism' == key2 %} +{{ 'Delay Mechanism'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'log-min-pdelay-req-interval' == key2 %} +{{ 'Log Min PDelay Req Interval'.ljust(30)}} {{ value2 }} +{% endif %} +{% if 'version-number' == key2 %} +{{ 'Version Number'.ljust(30)}} {{ value2 }} +{% endif %} +{% endfor %} +{% endfor %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/ptp_show.j2 b/src/CLI/renderer/templates/ptp_show.j2 new file mode 100755 index 0000000000..5fb4d8803b --- /dev/null +++ b/src/CLI/renderer/templates/ptp_show.j2 @@ -0,0 +1,19 @@ +{{'-----------------------------------------------------------'}} +{{'Interface'.ljust(20)}} {{'State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for i in value %} + +{% for key2,value2 in i.items() %} +{% for i2 in value2 %} + +{% if 'port-number' in i2.keys() and 'port-state' in i2.keys() %} +{{ i2['port-number'].ljust(20)}}{{ i2['port-state']}} +{% endif %} + +{% endfor %} +{% endfor %} + +{% endfor %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/ptp_tp_show.j2 b/src/CLI/renderer/templates/ptp_tp_show.j2 new file mode 100755 index 0000000000..12013673b5 --- /dev/null +++ b/src/CLI/renderer/templates/ptp_tp_show.j2 @@ -0,0 +1,30 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(20)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% if 'current-utc-offset-valid' in value.keys() %} +{{ 'Curr UTC Offset Vld'.ljust(20)}} {{ value['current-utc-offset-valid']}} +{% endif %} +{% if 'current-utc-offset' in value.keys() %} +{{ 'Curr UTC Offset'.ljust(20)}} {{ value['current-utc-offset']}} +{% endif %} +{% if 'leap59' in value.keys() %} +{{ 'Leap59'.ljust(20)}} {{ value['leap59']}} +{% endif %} +{% if 'leap61' in value.keys() %} +{{ 'Leap61'.ljust(20)}} {{ value['leap61']}} +{% endif %} +{% if 'time-traceable' in value.keys() %} +{{ 'Time Traceable'.ljust(20)}} {{ value['time-traceable']}} +{% endif %} +{% if 'frequency-traceable' in value.keys() %} +{{ 'Freq Traceable'.ljust(20)}} {{ value['frequency-traceable']}} +{% endif %} +{% if 'ptp-timescale' in value.keys() %} +{{ 'PTP Timescale'.ljust(20)}} {{ value['ptp-timescale']}} +{% endif %} +{% if 'time-source' in value.keys() %} +{{ 'Time Source'.ljust(20)}} {{ value['time-source']}} +{% endif %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/show_access_group.j2 b/src/CLI/renderer/templates/show_access_group.j2 new file mode 100644 index 0000000000..5d12b91ed6 --- /dev/null +++ b/src/CLI/renderer/templates/show_access_group.j2 @@ -0,0 +1,40 @@ +{% if json_output %} +{% for key in json_output %} + {# This condition checks if the JSON response has data from the acl/interface list #} + {% if "interface" in key %} + {% for interface in json_output[key] %} + {% set if_id = interface["id"] %} + {% if interface["ingress-acl-sets"] %} + {% set idirection = "ingress" %} + {% endif %} + {% if interface["egress-acl-sets"] %} + {% set edirection = "egress" %} + {% endif %} + {% if idirection %} + {% set ing_acl_sets = idirection + "-acl-sets" %} + {% set ing_acl_set = idirection + "-acl-set" %} + {% set ing_acl_set_list = interface[ing_acl_sets][ing_acl_set] %} + {% for ing_acl_set in ing_acl_set_list %} + {% set i_acl_name = ing_acl_set["set-name"] %} + {% if idirection == "ingress" %} + {% set idirection = "Ingress" %} + {% endif %} + {{- idirection }} IP access-list {{ i_acl_name }} on {{ if_id }} + {% endfor %} + {% endif %} + {% if edirection %} + {% set eg_acl_sets = edirection + "-acl-sets" %} + {% set eg_acl_set = edirection + "-acl-set" %} + {% set eg_acl_set_list = interface[eg_acl_sets][eg_acl_set] %} + {% for eg_acl_set in eg_acl_set_list %} + {% set e_acl_name = eg_acl_set["set-name"] %} + {% if edirection == "egress" %} + {% set edirection = "Egress" %} + {% endif %} + {{- edirection }} IP access-list {{ e_acl_name }} on {{ if_id }} + {% endfor %} + {% endif %} + {% endfor %} + {% endif %} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_access_list.j2 b/src/CLI/renderer/templates/show_access_list.j2 new file mode 100644 index 0000000000..47a57fa7e8 --- /dev/null +++ b/src/CLI/renderer/templates/show_access_list.j2 @@ -0,0 +1,74 @@ +{% macro traverse_acl_entry(acl_entry_list) -%} + {% for seq in acl_entry_list %} + {% set response_list = [] %} + {# Get sequence id #} + {% set seqid = seq["sequence-id"] %} + {% set _list = response_list.append( seqid ) %} + {# Get forwarding action #} + {% set fwd_action = seq["actions"]["config"]["forwarding-action"] %} + {%- if "ACCEPT" in fwd_action %} + {% set fwd_action = "permit" %} + {%- endif %} + {%- if "DROP" in fwd_action %} + {% set fwd_action = "deny" %} + {%- endif %} + {% set _list = response_list.append( fwd_action ) %} + {# Get protocol #} + {% set proto = seq["ipv4"]["state"]["protocol"].split(':')[1].split('_')[1]|lower %} + {% set _list = response_list.append( proto ) %} + {# Get Source IP #} + {% set src_ip = seq["ipv4"]["state"]["source-address"] %} + {% set _list = response_list.append( src_ip ) %} + {# include src port number if available #} + {%- if seq["transport"] %} + {%- if seq["transport"]["config"]["source-port"] %} + {% set src_port = "eq " + seq["transport"]["config"]["source-port"]|string %} + {% set _list = response_list.append( src_port ) %} + {%- endif %} + {%- endif %} + {# Get Destination IP #} + {% set dstn_ip = seq["ipv4"]["state"]["destination-address"] %} + {% set _list = response_list.append( dstn_ip ) %} + {# include dstn port number if available #} + {%- if seq["transport"] %} + {%- if seq["transport"]["config"]["destination-port"] %} + {% set dstn_port = "eq " + seq["transport"]["config"]["destination-port"]|string %} + {% set _list = response_list.append( dstn_port ) %} + {%- endif %} + {%- if seq["transport"]["config"]["tcp-flags"] %} + {% for var in seq["transport"]["config"]["tcp-flags"] %} + {% set flag = var.split(':')[1].split('_')[1]|lower %} + {% set _list = response_list.append( flag ) %} + {% endfor %} + {%- endif %} + {%- endif %} + {%- if seq["ipv4"]["state"]["dscp"] %} + {% set _list = response_list.append( "dscp "+seq["ipv4"]["state"]["dscp"]|string ) %} + {%- endif %} + {{- " " }} {{ response_list|join(' ') }} + {% endfor %} +{%- endmacro %} +{% for key in json_output %} + {# This condition checks if the JSON response has data from the acl-entry list #} + {% if "acl-entry" in key -%} + {% set acl_entry = json_output[key] -%} + {{ traverse_acl_entry(acl_entry) }} + {%- endif %} +{% endfor %} +{% for acl_sets in json_output -%} + {% if "acl-set" in acl_sets -%} + {# This condition checks if the JSON response has data from the acl-sets container output -#} + {% for acl_set in json_output[acl_sets] %} + {% if acl_set["state"] -%} + ip access-list {{ acl_set["state"]["name"] }} + {% set acl_entry_list = acl_set["acl-entries"] %} + {% if acl_entry_list -%} + {% for each in acl_entry_list -%} + {% set acl_entry = acl_entry_list[each] -%} + {{ traverse_acl_entry(acl_entry) }} + {%- endfor %} + {%- endif %} + {%- endif %} + {% endfor %} + {%- endif %} +{%- endfor %} diff --git a/src/CLI/renderer/templates/show_drop_monitor_flow.j2 b/src/CLI/renderer/templates/show_drop_monitor_flow.j2 new file mode 100644 index 0000000000..b7d5ef3d5b --- /dev/null +++ b/src/CLI/renderer/templates/show_drop_monitor_flow.j2 @@ -0,0 +1,34 @@ +{% set vars = {'flowName': ""} %} +{% set vars = {'aclTable': ""} %} +{% set vars = {'aclRule': ""} %} +{% set vars = {'collector': ""} %} +{% set vars = {'samplingRate': ""} %} +{{'------------------------------------------------------------------------------------------------------------------------'}} +{{'FLOW'.ljust(20)}} {{'ACL RULE'.ljust(20)}} {{'ACL TABLE'.ljust(20)}} {{'COLLECTOR'.ljust(20)}} {{'SAMPLING RATE'}} +{{'------------------------------------------------------------------------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for item in value %} +{% for key2,value2 in item.items() %} +{% if 'ietf-ts:flow-key' == key2 %} +{% if vars.update({'flowName':value2}) %}{% endif %} +{% endif %} +{% if 'ietf-ts:each-flow-data' == key2 %} +{% for key,value in value2.items() %} +{% if 'acl-rule' == key %} +{% if vars.update({'aclRuleName':value}) %}{% endif %} +{% endif %} +{% if 'acl-table' == key %} +{% if vars.update({'aclTableName':value}) %}{% endif %} +{% endif %} +{% if 'collector' == key %} +{% if vars.update({'collectorName':value}) %}{% endif %} +{% endif %} +{% if 'sample' == key %} +{% if vars.update({'samplingRate':value}) %}{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{{'%-25s'|format(vars.flowName)}}{{'%-20s'|format(vars.aclRuleName)}}{{'%-20s'|format(vars.aclTableName)}} {{'%-20s'|format(vars.collectorName)}} {{'%-20s'|format(vars.samplingRate)}} +{% endfor %} +{% endfor %} diff --git a/src/CLI/renderer/templates/show_flow.j2 b/src/CLI/renderer/templates/show_flow.j2 new file mode 100755 index 0000000000..b20c32065c --- /dev/null +++ b/src/CLI/renderer/templates/show_flow.j2 @@ -0,0 +1,27 @@ +{% set vars = {'flowName': ""} %} +{% set vars = {'aclTableName': ""} %} +{% set vars = {'aclRuleName': ""} %} +{{'-----------------------------------------------------------------------'}} +{{'NAME'.ljust(20)}} {{'ACL TABLE NAME'.ljust(30)}} {{'ACL RULE NAME'}} +{{'-----------------------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for item in value %} +{% for key2,value2 in item.items() %} +{% if 'ietf-ts:flow-key' == key2 %} +{% if vars.update({'flowName':value2}) %}{% endif %} +{% endif %} +{% if 'ietf-ts:each-flow-data' == key2 %} +{% for key,value in value2.items() %} +{% if 'acl-rule-name' == key %} +{% if vars.update({'aclRuleName':value}) %}{% endif %} +{% endif %} +{% if 'acl-table-name' == key %} +{% if vars.update({'aclTableName':value}) %}{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{{'%-25s'|format(vars.flowName)}}{{'%-30s'|format(vars.aclTableName)}}{{'%-4s'|format(vars.aclRuleName)}} +{% endfor %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/show_image.j2 b/src/CLI/renderer/templates/show_image.j2 new file mode 100755 index 0000000000..26b0b12773 --- /dev/null +++ b/src/CLI/renderer/templates/show_image.j2 @@ -0,0 +1,13 @@ +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% if key == 'Available' %} +{{key.ljust(20)}}: +{% for img in value %} +{{img}} +{% endfor %} +{% else %} +{{key.ljust(20)}}:{{value}} +{% endif %} +{% endfor %} +{{'-----------------------------------------------------------'}} + diff --git a/src/CLI/renderer/templates/show_interface.j2 b/src/CLI/renderer/templates/show_interface.j2 new file mode 100644 index 0000000000..8e50d6fc74 --- /dev/null +++ b/src/CLI/renderer/templates/show_interface.j2 @@ -0,0 +1,125 @@ +{% set vars = {'ipv4': ""} %} +{% set vars = {'ipv6': ""} %} +{% set vars = {'name': ""} %} +{% set vars = {'admin_state': ""} %} +{% set vars = {'oper_state': ""} %} +{% set vars = {'index': ""} %} +{% set vars = {'description':""} %} +{% set vars = {'mtu': ""} %} +{% set vars = {'ipv4_src_pfx': ""} %} +{% set vars = {'ipv6_src_pfx': ""} %} +{% set vars = {'mode4': "not-set"} %} +{% set vars = {'mode6': "not-set"} %} +{% set vars = {'speed': ""} %} +{% set vars = {'in_pkts':""} %} +{% set vars = {'in_octets':""} %} +{% set vars = {'in_multi':""} %} +{% set vars = {'in_broad':""} %} +{% set vars = {'in_uni':""} %} +{% set vars = {'in_errors':""} %} +{% set vars = {'in_discards':""} %} +{% set vars = {'out_pkts':""} %} +{% set vars = {'out_octets':""} %} +{% set vars = {'out_multi':""} %} +{% set vars = {'out_broad':""} %} +{% set vars = {'out_uni':""} %} +{% set vars = {'out_errors':""} %} +{% set vars = {'out_discards':""} %} +{% if json_output -%} +{% for key_json in json_output %} +{% set interface_list = json_output[key_json]["interface"] %} +{% for interface in interface_list %} + {% for key in interface %} + {% if "ethernet" in key %} + {% if vars.update({'speed':interface[key]["state"]["port-speed"]|replace("openconfig-if-ethernet:SPEED_", "")}) %}{% endif %} + {% endif %} + {% if "state" in key %} + {% if vars.update({'name':interface[key]["name"]}) %}{% endif %} + {% if vars.update({'admin_state':interface[key]["admin-status"]}) %}{% endif %} + {% if vars.update({'oper_state':interface[key]["oper-status"]}) %}{% endif %} + {% if vars.update({'index':interface[key]["ifindex"]}) %}{% endif %} + {% if vars.update({'description':interface[key]["description"]}) %}{% endif %} + {% if vars.update({'mtu':interface[key]["mtu"]}) %}{% endif %} + {% endif %} + {% if "counters" in interface["state"] %} + {% if vars.update({'in_pkts':interface["state"]["counters"]["in-pkts"]}) %}{% endif %} + {% if vars.update({'in_octets':interface["state"]["counters"]["in-octets"]}) %}{% endif %} + {% if vars.update({'in_multi':interface["state"]["counters"]["in-multicast-pkts"]}) %}{% endif %} + {% if vars.update({'in_broad':interface["state"]["counters"]["in-broadcast-pkts"]}) %}{% endif %} + {% if vars.update({'in_uni':interface["state"]["counters"]["in-unicast-pkts"]}) %}{% endif %} + {% if vars.update({'in_errors':interface["state"]["counters"]["in-errors"]}) %}{% endif %} + {% if vars.update({'in_discards':interface["state"]["counters"]["in-discards"]}) %}{% endif %} + {% if vars.update({'out_pkts':interface["state"]["counters"]["out-pkts"]}) %}{% endif %} + {% if vars.update({'out_octets':interface["state"]["counters"]["out-octets"]}) %}{% endif %} + {% if vars.update({'out_multi':interface["state"]["counters"]["out-multicast-pkts"]}) %}{% endif %} + {% if vars.update({'out_broad':interface["state"]["counters"]["out-broadcast-pkts"]}) %}{% endif %} + {% if vars.update({'out_uni':interface["state"]["counters"]["out-unicast-pkts"]}) %}{% endif %} + {% if vars.update({'out_errors':interface["state"]["counters"]["in-errors"]}) %}{% endif %} + {% if vars.update({'out_discards':interface["state"]["counters"]["out-discards"]}) %}{% endif %} + {% endif %} + {% if "subinterfaces" in key %} + {% for subinterface in interface[key] %} + {% set subif_list = interface[key][subinterface] %} + {% for subif in subif_list %} + {% if vars.update({'ipv4':"IPV4"}) %}{% endif %} + {% if vars.update({'ipv6':"IPV6"}) %}{% endif %} + {% if subif["openconfig-if-ip:ipv4"] %} + {% set ip_list = subif["openconfig-if-ip:ipv4"]["addresses"]["address"] %} + {% set ip_all = [] %} + {% for ip in ip_list %} + {% set ipfx = ip["state"]["ip"] + "/" + ip["state"]["prefix-length"]|string() %} + {{ ip_all.append(ipfx)|default("", True)}} + {% if vars.update({'mode4':"MANUAL"}) %}{% endif %} + {% endfor %} + {% if vars.update({'ipv4_src_pfx':ip_all|join(',')}) %}{% endif %} + {% else %} + {% if vars.update({'ipv4_src_pfx':""}) %}{% endif %} + {% if vars.update({'mode4':"not-set"}) %}{% endif %} + {% endif %} + {% if subif["openconfig-if-ip:ipv6"] %} + {% set ip_list = subif["openconfig-if-ip:ipv6"]["addresses"]["address"] %} + {% set ipv6_all = [] %} + {% for ip in ip_list %} + {% set ipfx = ip["state"]["ip"] + "/" + ip["state"]["prefix-length"]|string() %} + {{ ipv6_all.append(ipfx)|default("", True)}} + {% if vars.update({'mode6':"MANUAL"}) %}{% endif %} + {% endfor %} + {% if vars.update({'ipv6_src_pfx':ipv6_all|join(',')}) %}{% endif %} + {% else %} + {% if vars.update({'ipv6_src_pfx':""}) %}{% endif %} + {% if vars.update({'mode6':"not-set"}) %}{% endif %} + {% endif %} + {% endfor %} + {% endfor %} + {% endif %} + {% endfor %} +{% if 'Ethernet' in vars.name %} +{{ vars.name }} is {{ vars.admin_state|lower() }}, line protocol is {{vars.oper_state|lower() }} +Hardware is Eth +Interface index is {{ vars.index }} +{% if vars.description %} +Description: {{ vars.description }} +{% endif %} +{% if vars.ipv4_src_pfx %} +{{ vars.ipv4 }} address is {{ vars.ipv4_src_pfx }} +{% endif %} +Mode of {{ vars.ipv4 }} address assignment: {{ vars.mode4 }} +{% if vars.ipv6_src_pfx %} +{{ vars.ipv6 }} address is {{ vars.ipv6_src_pfx }} +{% endif %} +Mode of {{ vars.ipv6 }} address assignment: {{ vars.mode6 }} +IP MTU {{ vars.mtu }} bytes +LineSpeed {{ vars.speed }}, Auto-negotiation off +Input statistics: + {{vars.in_pkts}} packets, {{vars.in_octets}} octets + {{vars.in_multi}} Multicasts, {{vars.in_broad}} Broadcasts, {{vars.in_uni}} Unicasts + {{vars.in_errors}} error, {{vars.in_discards}} discarded +Output statistics: + {{vars.out_pkts}} packets, {{vars.out_octets}} octets + {{vars.out_multi}} Multicasts, {{vars.out_broad}} Broadcasts, {{vars.out_uni}} Unicasts + {{vars.out_errors}} error, {{vars.out_discards}} discarded + +{% endif %} +{% endfor %} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_interface_counters.j2 b/src/CLI/renderer/templates/show_interface_counters.j2 new file mode 100644 index 0000000000..0abdbce778 --- /dev/null +++ b/src/CLI/renderer/templates/show_interface_counters.j2 @@ -0,0 +1,39 @@ +{% set vars = {'name': ""} %} +{% set vars = {'oper_state': ""} %} +{% set vars = {'in_packets': ""} %} +{% set vars = {'in_errors': ""} %} +{% set vars = {'in_discards': ""} %} +{% set vars = {'out_packets': ""} %} +{% set vars = {'out_errors': ""} %} +{% set vars = {'out_discards': ""} %} +{% if json_output -%} +------------------------------------------------------------------------------------------------ +{{'%-15s'|format("Interface")}}{{'%-10s'|format("State")}}{{'%-10s'|format("RX_OK")}}{{'%-10s'|format("RX_ERR")}}{{'%-10s'|format("RX_DRP")}}{{'%-10s'|format("TX_OK")}}{{'%-10s'|format("TX_ERR")}}{{'%-10s'|format("TX_DRP")}} +------------------------------------------------------------------------------------------------ +{% for key_json in json_output %} +{% set interface_list = json_output[key_json]["interface"] %} +{% for interface in interface_list %} + {% for key in interface %} + {% if "state" in key %} + {% if vars.update({'name':interface[key]["name"]}) %}{% endif %} + {% if interface[key]["oper-status"] =='DOWN' %} + {% if vars.update({'oper_state':'D'}) %}{% endif %} + {% else %} + {% if vars.update({'oper_state':'U'}) %}{% endif %} + {% endif %} + {% endif %} + {% if "counters" in interface["state"] %} + {% if vars.update({'in_packets':interface["state"]["counters"]["in-pkts"]}) %}{% endif %} + {% if vars.update({'in_errors':interface["state"]["counters"]["in-errors"]}) %}{% endif %} + {% if vars.update({'in_discards':interface["state"]["counters"]["in-discards"]}) %}{% endif %} + {% if vars.update({'out_packets':interface["state"]["counters"]["out-pkts"]}) %}{% endif %} + {% if vars.update({'out_errors':interface["state"]["counters"]["out-errors"]}) %}{% endif %} + {% if vars.update({'out_discards':interface["state"]["counters"]["out-discards"]}) %}{% endif %} + {% endif %} + {% endfor %} +{% if 'Ethernet' in vars.name %} +{{'%-15s'|format(vars.name)}}{{'%-10s'|format(vars.oper_state)}}{{'%-10s'|format(vars.in_packets)}}{{'%-10s'|format(vars.in_errors)}}{{'%-10s'|format(vars.in_discards)}}{{'%-10s'|format(vars.out_packets)}}{{'%-10s'|format(vars.out_errors)}}{{'%-10s'|format(vars.out_discards)}} +{% endif %} +{% endfor %} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_interface_id.j2 b/src/CLI/renderer/templates/show_interface_id.j2 new file mode 100644 index 0000000000..b4b4b32eed --- /dev/null +++ b/src/CLI/renderer/templates/show_interface_id.j2 @@ -0,0 +1,121 @@ +{% set vars = {'ipv4': ""} %} +{% set vars = {'ipv6': ""} %} +{% set vars = {'name': ""} %} +{% set vars = {'admin_state': ""} %} +{% set vars = {'oper_state': ""} %} +{% set vars = {'index': ""} %} +{% set vars = {'description':""} %} +{% set vars = {'mtu': ""} %} +{% set vars = {'ipv4_src_pfx': ""} %} +{% set vars = {'ipv6_src_pfx': ""} %} +{% set vars = {'mode4': "not-set"} %} +{% set vars = {'mode6': "not-set"} %} +{% set vars = {'speed': ""} %} +{% set vars = {'in_pkts':""} %} +{% set vars = {'in_octets':""} %} +{% set vars = {'in_multi':""} %} +{% set vars = {'in_broad':""} %} +{% set vars = {'in_uni':""} %} +{% set vars = {'in_errors':""} %} +{% set vars = {'in_discards':""} %} +{% set vars = {'out_pkts':""} %} +{% set vars = {'out_octets':""} %} +{% set vars = {'out_multi':""} %} +{% set vars = {'out_broad':""} %} +{% set vars = {'out_uni':""} %} +{% set vars = {'out_errors':""} %} +{% set vars = {'out_discards':""} %} +{% if json_output -%} +{% for interfaces in json_output %} +{% set interface_list = json_output[interfaces] %} +{% for interface in interface_list %} + {% for key in interface %} + {% if "ethernet" in key %} + {% if vars.update({'speed':interface[key]["state"]["port-speed"]|replace("openconfig-if-ethernet:SPEED_", "")}) %}{% endif %} + {% endif %} + {% if "state" in key %} + {% if vars.update({'name':interface[key]["name"]}) %}{% endif %} + {% if vars.update({'admin_state':interface[key]["admin-status"]}) %}{% endif %} + {% if vars.update({'oper_state':interface[key]["oper-status"]}) %}{% endif %} + {% if vars.update({'index':interface[key]["ifindex"]}) %}{% endif %} + {% if vars.update({'description':interface[key]["description"]}) %}{% endif %} + {% if vars.update({'mtu':interface[key]["mtu"]}) %}{% endif %} + {% if vars.update({'in_pkts':interface[key]["counters"]["in-pkts"]}) %}{% endif %} + {% if vars.update({'in_octets':interface[key]["counters"]["in-octets"]}) %}{% endif %} + {% if vars.update({'in_multi':interface[key]["counters"]["in-multicast-pkts"]}) %}{% endif %} + {% if vars.update({'in_broad':interface[key]["counters"]["in-broadcast-pkts"]}) %}{% endif %} + {% if vars.update({'in_uni':interface[key]["counters"]["in-unicast-pkts"]}) %}{% endif %} + {% if vars.update({'in_errors':interface[key]["counters"]["in-errors"]}) %}{% endif %} + {% if vars.update({'in_discards':interface[key]["counters"]["in-discards"]}) %}{% endif %} + {% if vars.update({'out_pkts':interface[key]["counters"]["out-pkts"]}) %}{% endif %} + {% if vars.update({'out_octets':interface[key]["counters"]["out-octets"]}) %}{% endif %} + {% if vars.update({'out_multi':interface[key]["counters"]["out-multicast-pkts"]}) %}{% endif %} + {% if vars.update({'out_broad':interface[key]["counters"]["out-broadcast-pkts"]}) %}{% endif %} + {% if vars.update({'out_uni':interface[key]["counters"]["out-unicast-pkts"]}) %}{% endif %} + {% if vars.update({'out_errors':interface[key]["counters"]["in-errors"]}) %}{% endif %} + {% if vars.update({'out_discards':interface[key]["counters"]["out-discards"]}) %}{% endif %} + {% endif %} + {% if "subinterfaces" in key %} + {% for subinterface in interface[key] %} + {% set subif_list = interface[key][subinterface] %} + {% for subif in subif_list %} + {% if vars.update({'ipv4':"IPV4"}) %}{% endif %} + {% if vars.update({'ipv6':"IPV6"}) %}{% endif %} + {% if subif["openconfig-if-ip:ipv4"] %} + {% set ip_list = subif["openconfig-if-ip:ipv4"]["addresses"]["address"] %} + {% set ip_all = [] %} + {% for ip in ip_list %} + {% set ipfx = ip["state"]["ip"] + "/" + ip["state"]["prefix-length"]|string() %} + {{ ip_all.append(ipfx)|default("", True)}} + {% if vars.update({'mode4':"MANUAL"}) %}{% endif %} + {% endfor %} + {% if vars.update({'ipv4_src_pfx':ip_all|join(',')}) %}{% endif %} + {% else %} + {% if vars.update({'mode4':"not-set"}) %}{% endif %} + {% endif %} + {% if subif["openconfig-if-ip:ipv6"] %} + {% set ip_list = subif["openconfig-if-ip:ipv6"]["addresses"]["address"] %} + {% set ipv6_all = [] %} + {% for ip in ip_list %} + {% set ipfx = ip["state"]["ip"] + "/" + ip["state"]["prefix-length"]|string() %} + {{ ipv6_all.append(ipfx)|default("", True)}} + {% if vars.update({'mode6':"MANUAL"}) %}{% endif %} + {% endfor %} + {% if vars.update({'ipv6_src_pfx':ipv6_all|join(',')}) %}{% endif %} + {% else %} + {% if vars.update({'mode6':"not-set"}) %}{% endif %} + {% endif %} + {% endfor %} + {% endfor %} + {% endif %} + {% endfor %} +{% if vars.name %} +{{ vars.name }} is {{ vars.admin_state|lower() }}, line protocol is {{vars.oper_state|lower() }} +Hardware is Eth + +Interface index is {{ vars.index }} +{% if vars.description %} +Description: {{ vars.description }} +{% endif %} +{% if vars.ipv4_src_pfx %} +{{ vars.ipv4 }} address is {{ vars.ipv4_src_pfx }} +{% endif %} +Mode of {{ vars.ipv4 }} address assignment: {{ vars.mode4 }} +{% if vars.ipv6_src_pfx %} +{{ vars.ipv6 }} address is {{ vars.ipv6_src_pfx }} +{% endif %} +Mode of {{ vars.ipv6 }} address assignment: {{ vars.mode6 }} +IP MTU {{ vars.mtu }} bytes +LineSpeed {{ vars.speed }}, Auto-negotiation off +Input statistics: + {{vars.in_pkts}} packets, {{vars.in_octets}} octets + {{vars.in_multi}} Multicasts, {{vars.in_broad}} Broadcasts, {{vars.in_uni}} Unicasts + {{vars.in_errors}} error, {{vars.in_discards}} discarded +Output statistics: + {{vars.out_pkts}} packets, {{vars.out_octets}} octets + {{vars.out_multi}} Multicasts, {{vars.out_broad}} Broadcasts, {{vars.out_uni}} Unicasts + {{vars.out_errors}} error, {{vars.out_discards}} discarded +{%- endif %} +{% endfor %} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_interface_status.j2 b/src/CLI/renderer/templates/show_interface_status.j2 new file mode 100644 index 0000000000..caf9fde229 --- /dev/null +++ b/src/CLI/renderer/templates/show_interface_status.j2 @@ -0,0 +1,38 @@ +{% set vars = {'name': ""} %} +{% set vars = {'admin_state': ""} %} +{% set vars = {'oper_state': ""} %} +{% set vars = {'description': ""} %} +{% set vars = {'mtu': ""} %} +{% set vars = {'speed': ""} %} +{% set vars = {'parsedSpeed': ""} %} +{% if json_output -%} +------------------------------------------------------------------------------------------ +{{'%-20s'|format("Name")}}{{'%-20s'|format("Description")}}{{'%-15s'|format("Admin")}}{{'%-15s'|format("Oper")}}{{'%-15s'|format("Speed")}}{{'%-15s'|format("MTU")}} +------------------------------------------------------------------------------------------ +{% for key_json in json_output %} +{% set interface_list = json_output[key_json]["interface"] %} +{% for interface in interface_list %} + {% for key in interface %} + {% if "ethernet" in key %} + {% if vars.update({'speed':interface[key]["state"]["port-speed"]|replace("openconfig-if-ethernet:SPEED_", "")}) %} {% endif %} + {% if vars.update({'parsedSpeed':vars.speed|replace("B", "")}) %} {% endif %} + {% endif %} + {% if "state" in key %} + {% if vars.update({'name':interface[key]["name"]}) %}{% endif %} + {% if vars.update({'admin_state':interface[key]["admin-status"]}) %}{% endif %} + {% if vars.update({'oper_state':interface[key]["oper-status"]}) %}{% endif %} + {% if vars.update({'mtu':interface[key]["mtu"]}) %}{% endif %} + {% if interface[key]["description"] != "" %} + {% if vars.update({'description':interface[key]["description"]}) %}{% endif %} + {%else %} + {% if vars.update({'description':"-"}) %}{% endif %} + {% endif %} + {% endif %} + {% endfor %} +{% if 'Ethernet' in vars.name %} +{{'%-20s'|format(vars.name)}}{{'%-20s'|format(vars.description)}}{{'%-15s'|format(vars.admin_state|lower())}}{{'%-15s'|format(vars.oper_state|lower())}}{{'%-15s'|format(vars.parsedSpeed)}}{{'%-15s'|format(vars.mtu)}} +{% endif %} +{% endfor %} +{% endfor %} +{% endif %} + diff --git a/src/CLI/renderer/templates/show_mac.j2 b/src/CLI/renderer/templates/show_mac.j2 new file mode 100644 index 0000000000..060c0fb301 --- /dev/null +++ b/src/CLI/renderer/templates/show_mac.j2 @@ -0,0 +1,7 @@ +{{'-----------------------------------------------------------'}} +{{'VLAN'.ljust(12)}} {{'MAC-ADDRESS'.ljust(20)}}{{'TYPE'.ljust(12)}} {{'INTERFACE'.ljust(20)}} +{{'-----------------------------------------------------------'}} +{% for mac_list in json_output %} +{{(mac_list['Vlan'] | string).ljust(12)}}{{(mac_list['mac-address'] | string).ljust(20)}}{{(mac_list['entry-type'] | string).ljust(14)}}{{(mac_list['port'] | string).ljust(20)}} +{% endfor %} + diff --git a/src/CLI/renderer/templates/show_mac_count.j2 b/src/CLI/renderer/templates/show_mac_count.j2 new file mode 100644 index 0000000000..fd1bc78a3b --- /dev/null +++ b/src/CLI/renderer/templates/show_mac_count.j2 @@ -0,0 +1,6 @@ +{% for mac_list in json_output %} +{{'MAC Entries for all vlans : '.ljust(20)}} {{(mac_list['vlan-mac'] | string).ljust(20)}} +{{'Dynamic Address Count : '.ljust(20)}} {{(mac_list['dynamic-mac'] | string).ljust(20)}} +{{'Static Address (User-defined) Count : '.ljust(20)}} {{(mac_list['static-mac'] | string).ljust(20)}} +{{'Total MAC Addresses in Use:'.ljust(20)}} {{(mac_list['total-mac'] | string).ljust(20)}} +{% endfor %} diff --git a/src/CLI/renderer/templates/show_portchannel.j2 b/src/CLI/renderer/templates/show_portchannel.j2 new file mode 100644 index 0000000000..6ff29b1f31 --- /dev/null +++ b/src/CLI/renderer/templates/show_portchannel.j2 @@ -0,0 +1,32 @@ +{% set just_var = 2 %} +{% set vars = {'oper_state': ""} %} +{% set vars = {'protocol': ""} %} + +Flags: D - Down + U - Up + +{{'----------------------------------------------------------------------------------------------------------------------------'}} +{{'%-20s'|format("Group")}}{{'%-30s'|format("PortChannel")}}{{'%-20s'|format("Type")}}{{'%-15s'|format("Protocol")}}{{'%-15s'|format("Member Ports")}} +{{'----------------------------------------------------------------------------------------------------------------------------'}} +{% for dict in json_output %} +{% if dict["name"] == 'lacp' %} +{% if vars.update({'protocol':'DYNAMIC'}) %}{% endif %} +{% else %} +{% if vars.update({'protocol':'STATIC'}) %}{% endif %} +{% endif %} +{% if dict["oper_status"] == 'down' %} +{% if vars.update({'oper_state':'D'}) %}{% endif %} +{% else %} +{% if vars.update({'oper_state':'U'}) %}{% endif %} +{% endif %} +{% if dict['members']|length > 0 %} +{{'%-20s'|format(dict['lagname'].strip('PortChannel'))}}{{'%-15s'|format(dict['lagname'])}}{{'%-15s'|format("("+vars.oper_state+")")}}{{'%-20s'|format(dict['type'])}}{{'%-15s'|format(vars.protocol)}}{{'%-15s'|format(dict['members'][0]|string|replace("[", "")|replace("]", ""))}} +{% if dict['members']|length > 1 %} +{% for i in dict['members'][1:] %} +{{ '%95s'|format(i) }} +{% endfor %} +{% endif %} +{% else %} +{{'%-20s'|format(dict['lagname'].strip('PortChannel'))}}{{'%-15s'|format(dict['lagname'])}}{{'%-15s'|format("("+vars.oper_state+")")}}{{'%-20s'|format(dict['type'])}}{{'%-15s'|format(vars.protocol)}}{{'%-15s'|format(dict['members']|string|replace("[", "")|replace("]", ""))}} +{% endif %} +{% endfor %} diff --git a/src/CLI/renderer/templates/show_sample.j2 b/src/CLI/renderer/templates/show_sample.j2 new file mode 100644 index 0000000000..0c7a21ae82 --- /dev/null +++ b/src/CLI/renderer/templates/show_sample.j2 @@ -0,0 +1,22 @@ +{% set vars = {'sampleName': ""} %} +{% set vars = {'samplingRate': ""} %} +{{'------------------------------------------------------------------------------------------------------------------------'}} +{{'NAME'.ljust(20)}} {{'SAMPLING RATE'}} +{{'------------------------------------------------------------------------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for item in value %} +{% for key2,value2 in item.items() %} +{% if 'ietf-ts:sample-key' == key2 %} +{% if vars.update({'sampleName':value2}) %}{% endif %} +{% endif %} +{% if 'ietf-ts:each-sample-data' == key2 %} +{% for key,value in value2.items() %} +{% if 'sampling-rate' == key %} +{% if vars.update({'samplingRate':value}) %}{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{{'%-25s'|format(vars.sampleName)}} {{'%-20s'|format(vars.samplingRate)}} +{% endfor %} +{% endfor %} diff --git a/src/CLI/renderer/templates/show_sflow.j2 b/src/CLI/renderer/templates/show_sflow.j2 new file mode 100755 index 0000000000..3735f70bd9 --- /dev/null +++ b/src/CLI/renderer/templates/show_sflow.j2 @@ -0,0 +1,14 @@ +{{'---------------------------------------------------------'}} +{{'Global sFlow Information'.ljust(20)}} +{{'---------------------------------------------------------'}} +{% if json_output and 'sflow' in json_output -%} +{% set sflow_info = json_output['sflow'] %} +{% set sflow_col_info = json_output['col_info'] %} +{{' admin state: '}}{{sflow_info['admin_state']}} +{{' polling-interval: '}}{{sflow_info['polling_interval']}} +{{' agent-id: '}} {{sflow_info['agent_id']}} +{{' configured collectors: '}} {{sflow_col_info['col_cnt']}} +{% for collector in sflow_col_info['col_lst'] %} + {{collector['collector_name']}} {{collector['collector_ip']}} {{collector['collector_port']}} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_sflow_intf.j2 b/src/CLI/renderer/templates/show_sflow_intf.j2 new file mode 100755 index 0000000000..871d02ce92 --- /dev/null +++ b/src/CLI/renderer/templates/show_sflow_intf.j2 @@ -0,0 +1,9 @@ +{{'-----------------------------------------------------------'}} +{{'sFlow interface configurations'.ljust(20)}} +{{'%40s'|format('Interface Admin State Sampling Rate')}} +{% if json_output -%} +{% for sess in json_output %} +{{'%12s'|format(sess['ifname'])}} {{sess['admin_state']}} {{sess['sample_rate']}} +{% endfor %} +{% endif %} + diff --git a/src/CLI/renderer/templates/show_statistics_flow.j2 b/src/CLI/renderer/templates/show_statistics_flow.j2 new file mode 100755 index 0000000000..ad4486a74a --- /dev/null +++ b/src/CLI/renderer/templates/show_statistics_flow.j2 @@ -0,0 +1,35 @@ +{% set vars = {'flowName': ""} %} +{% set vars = {'aclTableName': ""} %} +{% set vars = {'aclRuleName': ""} %} +{% set vars = {'statPacketsCount': ""} %} +{% set vars = {'statBytesCount': ""} %} +{{'------------------------------------------------------------------------------------------------------------------------'}} +{{'FLOW NAME'.ljust(20)}} {{'RULE NAME'.ljust(20)}} {{'TABLE NAME'.ljust(20)}} {{'PACKETS COUNT'.ljust(20)}} {{'BYTES COUNT'}} +{{'------------------------------------------------------------------------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% for item in value %} +{% for key2,value2 in item.items() %} +{% if 'flow-name' == key2 %} +{% if vars.update({'flowName':value2}) %}{% endif %} +{% endif %} +{% if 'rule-name' == key2 %} +{% if vars.update({'aclRuleName':value2}) %}{% endif %} +{% endif %} +{% if 'table-name' == key2 %} +{% if vars.update({'aclTableName':value2}) %}{% endif %} +{% endif %} +{% if 'ietf-ts:ifa-stats' == key2 %} +{% for key,value in value2.items() %} +{% if 'Packets' == key %} +{% if vars.update({'statPacketsCount':value}) %}{% endif %} +{% endif %} +{% if 'Bytes' == key %} +{% if vars.update({'statBytesCount':value}) %}{% endif %} +{% endif %} +{% endfor %} +{% endif %} +{% endfor %} +{{'%-25s'|format(vars.flowName)}}{{'%-20s'|format(vars.aclRuleName)}}{{'%-20s'|format(vars.aclTableName)}} {{'%-20s'|format(vars.statPacketsCount)}} {{'%-20s'|format(vars.statBytesCount)}} +{% endfor %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/show_status.j2 b/src/CLI/renderer/templates/show_status.j2 new file mode 100755 index 0000000000..e3bf9497c3 --- /dev/null +++ b/src/CLI/renderer/templates/show_status.j2 @@ -0,0 +1,15 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(20)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{% if 'num-of-flows' in value.keys() %} +{{ 'Number of flows'.ljust(21)}} - {{ value['num-of-flows']}} +{% endif %} +{% if 'enable' in value.keys() %} +{{ 'Feature Enabled'.ljust(21)}} - {{ value['enable']}} +{% endif %} +{% if 'deviceid' in value.keys() %} +{{ 'Device Identifier'.ljust(21)}} - {{ value['deviceid']}} +{% endif %} +{% endfor %} + diff --git a/src/CLI/renderer/templates/show_stp.j2 b/src/CLI/renderer/templates/show_stp.j2 new file mode 100644 index 0000000000..78b610e554 --- /dev/null +++ b/src/CLI/renderer/templates/show_stp.j2 @@ -0,0 +1,206 @@ +{% set vars = {'stp_instance': ""} %} +{% set vars = {'vlan_name': ""} %} +{% set vars = {'intf_name': ""} %} +{% set vars = {'intf_pri': ""} %} +{% set vars = {'intf_path_cost': ""} %} +{% set vars = {'intf_p2p': ""} %} +{% set vars = {'intf_edge': ""} %} +{% set vars = {'intf_bpdu_filter': ""} %} +{% set vars = {'intf_role': ""} %} +{% set vars = {'intf_state': "" } %} +{% set vars = {'intf_desig_cost': "" } %} +{% set vars = {'intf_desig_bridge': "" } %} +{% set vars = {'br_id':"" } %} +{% set vars = {'br_mAge':""} %} +{% set vars = {'br_hello':""} %} +{% set vars = {'br_fDly':""} %} +{% set vars = {'br_holdtime':""} %} +{% set vars = {'lastTc':""} %} +{% set vars = {'tcChgCnt':""} %} +{% set vars = {'root_br':""}%} +{% set vars = {'root_path':""}%} +{% set vars = {'desig_br':""}%} +{% set vars = {'root_port_name':""}%} +{% set vars = {'max_age':""}%} +{% set vars = {'hello':""}%} +{% set vars = {'fDly':""}%} +{% if json_output -%} +{% if json_output is iterable %} +{% if "openconfig-spanning-tree:enabled-protocol" is in json_output %} +{% set stp_protocol = json_output['openconfig-spanning-tree:enabled-protocol'][0]%} +{%endif%} +{% set stp_intfs = {} %} +{% set vlan_list = {} %} +{% if "openconfig-spanning-tree:interfaces" is in json_output and json_output['openconfig-spanning-tree:interfaces'] is not none %} +{% set stp_intfs = json_output['openconfig-spanning-tree:interfaces']['interface'] %} +{%endif%} +{% if "openconfig-spanning-tree:interface" is in json_output and json_output['openconfig-spanning-tree:interface'] is not none %} +{% set stp_intfs = json_output['openconfig-spanning-tree:interface'] %} +{%endif%} +{% if stp_protocol == "openconfig-spanning-tree-ext:PVST" %} +Spanning-tree Mode: PVST +{% if "openconfig-spanning-tree-ext:pvst" is in json_output %} +{% set vlan_list = json_output['openconfig-spanning-tree-ext:pvst']['vlan'] %} +{%endif%} +{% if "openconfig-spanning-tree-ext:vlan" is in json_output %} +{% set vlan_list = json_output['openconfig-spanning-tree-ext:vlan'] %} +{%endif%} +{% for vlan in vlan_list %} + {% if vars.update({'vlan_name':vlan['vlan-id']}) %}{% endif %} + {% if vars.update({'stp_instance':vlan['state']['stp-instance']}) %}{% endif %} +{{' '}} +VLAN {{'%s'| format(vars.vlan_name)}} - STP instance {{'%s'| format(vars.stp_instance)}} +{{ '------------------------------------------------------------------------------------------------------------ ' }} +STP Bridge Parameters: +{{ ' ' }} +{{'%-18s'|format("Bridge")}}{{'%-10s'|format("Bridge")}}{{'%-10s'|format("Bridge")}}{{'%-10s'|format("Bridge")}}{{'%-6s'|format("Hold")}}{{'%-14s'|format("LastTopology")}}{{'%-10s'|format("Topology")}} +{{'%-18s'|format("Identifier")}}{{'%-10s'|format("MaxAge")}}{{'%-10s'|format("Hello")}}{{'%-10s'|format("FwdDly")}}{{'%-6s'|format("Time")}}{{'%-14s'|format("Change")}}{{'%-10s'|format("Change")}} +{{'%-18s'|format("hex")}}{{'%-10s'|format("sec")}}{{'%-10s'|format("sec")}}{{'%-10s'|format("sec")}}{{'%-6s'|format("sec")}}{{'%-14s'|format("sec")}}{{'%-10s'|format("cnt")}} + {% if vars.update({'br_id':vlan['state']['bridge-address']}) %}{% endif %} + {% if vars.update({'br_mAge':vlan['config']['max-age']}) %}{% endif %} + {% if vars.update({'br_hello':vlan['config']['hello-time']}) %}{% endif %} + {% if vars.update({'br_fDly':vlan['config']['forwarding-delay']}) %}{% endif %} + {% if vars.update({'br_holdtime':vlan['state']['hold-time']}) %}{% endif %} + {% if vars.update({'lastTc':vlan['state']['last-topology-change']}) %}{% endif %} + {% if vars.update({'tcChgCnt':vlan['state']['topology-changes']}) %}{% endif %} +{{'%-18s'|format(vars.br_id)}}{{'%-10s'|format(vars.br_mAge)}}{{'%-10s'|format(vars.br_hello)}}{{'%-10s'|format(vars.br_fDly)}}{{'%-6s'|format(vars.br_holdtime)}}{{'%-14s'|format(vars.lastTc)}}{{'%-10s'|format(vars.tcChgCnt)}} +{{ ' ' }} +{{'%-18s'|format("RootBridge")}}{{'%-10s'|format("RootPath")}}{{'%-18s'|format("DesignatedBridge")}}{{'%-12s'|format("Root")}}{{'%-5s'|format("Max")}}{{'%-5s'|format("Hel")}}{{'%-5s'|format("Fwd")}} +{{'%-18s'|format("Identifier")}}{{'%-10s'|format("Cost")}}{{'%-18s'|format("Identifier")}}{{'%-12s'|format("Port")}}{{'%-5s'|format("Age")}}{{'%-4s'|format("lo")}} {{'%-5s'|format("Dly")}} +{{'%-18s'|format("hex")}}{{'%-10s'|format("")}}{{'%-18s'|format("hex")}}{{'%-12s'|format("")}}{{'%-5s'|format("sec")}}{{'%-4s'|format("sec")}} {{'%-5s'|format("sec")}} + {% if vars.update({'root_br':vlan['state']['bridge-address']}) %}{% endif %} + {% if vars.update({'root_path':vlan['state']['root-cost']}) %}{% endif %} + {% if vars.update({'desig_br':vlan['state']['designated-root-address']}) %}{% endif %} + {% if vars.update({'root_port_name':vlan['state']['root-port-name']}) %}{% endif %} + {% if vars.update({'max_age':vlan['state']['max-age']}) %}{% endif %} + {% if vars.update({'hello':vlan['state']['hello-time']}) %}{% endif %} + {% if vars.update({'fDly':vlan['state']['forwarding-delay']}) %}{% endif %} +{{'%-18s'|format(vars.root_br)}}{{'%-10s'|format(vars.root_path)}}{{'%-18s'|format(vars.desig_br)}}{{'%-12s'|format(vars.root_port_name)}}{{'%-5s'|format(vars.max_age)}}{{'%-5s'|format(vars.hello)}}{{'%-5s'|format(vars.fDly)}} +{{' ' }} +STP Port Parameters: +{{'%-16s'|format("Port")}}{{'%-5s'|format("Prio")}}{{'%-10s'|format("Path")}}{{'%-5s'|format("Port")}}{{'%-7s'|format("Uplink")}}{{'%-7s'|format("BPDU")}}{{'%-11s'|format("State")}}{{'%-11s'|format("Designated")}}{{'%-17s'|format("Designated")}}{{'%-17s'|format("Designated")}} +{{'%-16s'|format("Num")}}{{'%-5s'|format("rity")}}{{'%-10s'|format("Cost")}}{{'%-5s'|format("Fast")}}{{'%-7s'|format("Fast")}}{{'%-7s'|format("Filter")}}{{'%-11s'|format("")}}{{'%-11s'|format("Cost")}}{{'%-17s'|format("Root")}}{{'%-17s'|format("bridge")}} +{% set interface_list = {} %} +{% if "interfaces" in vlan and "interface" in vlan['interfaces'] %} + {% set interface_list = vlan['interfaces']['interface'] %} +{%endif%} + {% for stp_intf in stp_intfs %} + {% for intf in interface_list %} + {% if stp_intf['name'] == intf['name'] %} + {% set vars = {'intf_fast': ""} %} + {% set vars = {'intf_ufast':""} %} + {% if vars.update({'intf_name':intf['name']}) %}{% endif %} + {% if intf['state'] %} + {% if vars.update({'intf_pri':intf['state']['port-priority']}) %}{% endif %} + {% if vars.update({'intf_path_cost':intf['state']['cost']}) %}{% endif %} + {% if vars.update({'intf_state':intf['state']['port-state']| replace('openconfig-spanning-tree-types:',"")}) %}{% endif %} + {% if vars.update({'intf_desig_cost':intf['state']['designated-cost']}) %}{% endif %} + {% if vars.update({'intf_desig_root':intf['state']['designated-root-address']}) %}{% endif %} + {% if vars.update({'intf_desig_bridge':intf['state']['designated-bridge-address']}) %}{% endif %} + {%endif %} + {% if stp_intf['config']['bpdu-filter'] == true %} + {% if vars.update({'intf_bpdu_filter': "Y"}) %}{% endif %} + {%else %} + {% if vars.update({'intf_bpdu_filter': "N"}) %}{% endif %} + {%endif %} + {% if stp_intf['config']['portfast'] == true %} + {% if vars.update({'intf_fast': "Y"}) %}{% endif %} + {%else %} + {% if vars.update({'intf_fast': "N"}) %}{% endif %} + {%endif %} + {% if stp_intf['config']['uplink-fast'] == true %} + {% if vars.update({'intf_ufast': "Y"}) %}{% endif %} + {%else %} + {% if vars.update({'intf_ufast': "N"}) %}{% endif %} + {%endif %} +{{'%-16s'|format(vars.intf_name)}}{{'%-5s'|format(vars.intf_pri)}}{{'%-10s'|format(vars.intf_path_cost)}}{{'%-5s'|format(vars.intf_fast)}}{{'%-7s'|format(vars.intf_ufast)}}{{'%-7s'|format(vars.intf_bpdu_filter)}}{{'%-11s'|format(vars.intf_state)}}{{'%-11s'|format(vars.intf_desig_cost)}}{{'%-17s'|format(vars.intf_desig_root)}}{{'%-17s'|format(vars.intf_desig_bridge)}} + {%endif %} + {%endfor %} + {% endfor %} + {% endfor %} +{% endif %} +{% if stp_protocol == "openconfig-spanning-tree-types:RAPID_PVST" %} +{% if "openconfig-spanning-tree:rapid-pvst" is in json_output and json_output['openconfig-spanning-tree:rapid-pvst'] is not none %} +{% set vlan_list = json_output['openconfig-spanning-tree:rapid-pvst']['vlan'] %} +{%endif%} +{% if "openconfig-spanning-tree:vlan" is in json_output and json_output['openconfig-spanning-tree:vlan']is not none %} +{% set vlan_list = json_output['openconfig-spanning-tree:vlan'] %} +{%endif%} +Spanning-tree Mode: RPVST +{% for vlan in vlan_list %} + {% set vars = {'br_txHCnt':""} %} + {% if vars.update({'vlan_name':vlan['vlan-id']}) %}{% endif %} + {% if vars.update({'stp_instance':vlan['state']['openconfig-spanning-tree-ext:stp-instance']}) %}{% endif %} +{{ " " }} +VLAN {{'%s'| format(vars.vlan_name)}} - RSTP instance {{'%s'| format(vars.stp_instance)}} +{{ '------------------------------------------------------------------------------------------------------------ ' }} +{{ ' ' }} +RSTP (IEEE 802.1w) Bridge Parameters: +{{ ' ' }} +{{'%-18s'|format("Bridge")}}{{'%-10s'|format("Bridge")}}{{'%-10s'|format("Bridge")}}{{'%-10s'|format("Bridge")}}{{'%-6s'|format("tx")}} +{{'%-18s'|format("Identifier")}}{{'%-10s'|format("MaxAge")}}{{'%-10s'|format("Hello")}}{{'%-10s'|format("FwdDly")}}{{'%-6s'|format("Hold")}} +{{'%-18s'|format("hex")}}{{'%-10s'|format("sec")}}{{'%-10s'|format("sec")}}{{'%-10s'|format("sec")}}{{'%-6s'|format("cnt")}} + {% if vars.update({'br_id':vlan['state']['bridge-address']}) %}{% endif %} + {% if vars.update({'br_mAge':vlan['config']['max-age']}) %}{% endif %} + {% if vars.update({'br_hello':vlan['config']['hello-time']}) %}{% endif %} + {% if vars.update({'br_fDly':vlan['config']['forwarding-delay']}) %}{% endif %} + {% if vars.update({'br_txHCnt':vlan['state']['tx-hold-count']}) %}{% endif %} +{{'%-18s'|format(vars.br_id)}}{{'%-10s'|format(vars.br_mAge)}}{{'%-10s'|format(vars.br_hello)}}{{'%-10s'|format(vars.br_fDly)}}{{'%-6s'|format(vars.br_txHCnt)}} +{{ ' ' }} +{{'%-18s'|format("RootBridge")}}{{'%-10s'|format("RootPath")}}{{'%-18s'|format("DesignatedBridge")}}{{'%-12s'|format("Root")}}{{'%-5s'|format("Max")}}{{'%-5s'|format("Hel")}}{{'%-5s'|format("Fwd")}} +{{'%-18s'|format("Identifier")}}{{'%-10s'|format("Cost")}}{{'%-18s'|format("Identifier")}}{{'%-12s'|format("Port")}}{{'%-5s'|format("Age")}}{{'%-4s'|format("lo")}} {{'%-5s'|format("Dly")}} +{{'%-18s'|format("hex")}}{{'%-10s'|format("")}}{{'%-18s'|format("hex")}}{{'%-12s'|format("")}}{{'%-5s'|format("sec")}}{{'%-4s'|format("sec")}} {{'%-5s'|format("sec")}} + {% if vars.update({'root_br':vlan['state']['bridge-address']}) %}{% endif %} + {% if vars.update({'root_path':vlan['state']['root-cost']}) %}{% endif %} + {% if vars.update({'desig_br':vlan['state']['designated-root-address']}) %}{% endif %} + {% if vars.update({'root_port_name_name':vlan['state']['openconfig-spanning-tree-ext:root-port-name']}) %}{% endif %} + {% if vars.update({'max_age':vlan['state']['max-age']}) %}{% endif %} + {% if vars.update({'hello':vlan['state']['hello-time']}) %}{% endif %} + {% if vars.update({'fDly':vlan['state']['forwarding-delay']}) %}{% endif %} +{{'%-18s'|format(vars.root_br)}}{{'%-10s'|format(vars.root_path)}}{{'%-18s'|format(vars.desig_br)}}{{'%-12s'|format(vars.root_port_name)}}{{'%-5s'|format(vars.max_age)}}{{'%-5s'|format(vars.hello)}}{{'%-5s'|format(vars.fDly)}} +{{' ' }} +RSTP (IEEE 802.1w) Port Parameters: +{{'%-16s'|format("Port")}}{{'%-5s'|format("Prio")}}{{'%-10s'|format("Path")}}{{'%-4s'|format("P2P")}}{{'%-5s'|format("Edge")}}{{'%-7s'|format("BPDU")}}{{'%-10s'|format("Role")}}{{'%-11s'|format("State")}}{{'%-11s'|format("Designa-")}}{{'%-17s'|format("Designated")}} +{{'%-16s'|format("Num")}}{{'%-5s'|format("rity")}}{{'%-10s'|format("Cost")}}{{'%-4s'|format("Mac")}}{{'%-5s'|format("Port")}}{{'%-7s'|format("Filter")}}{{'%-10s'|format("")}}{{'%-11s'|format("")}}{{'%-11s'|format("ted cost")}}{{'%-17s'|format("bridge")}} + +{% set interface_list = {} %} +{% if "interfaces" in vlan and "interface" in vlan['interfaces'] %} + {% set interface_list = vlan['interfaces']['interface'] %} +{%endif%} + {% for stp_intf in stp_intfs %} + {% for intf in interface_list %} + {% if stp_intf['name'] == intf['name'] %} + {% set vars = {'intf_p2pmac': ""} %} + {% set vars = {'intf_edge': ""} %} + {% if vars.update({'intf_name':intf['name']}) %}{% endif %} + {% if intf['state'] %} + {% if vars.update({'intf_pri':intf['state']['port-priority']}) %}{% endif %} + {% if vars.update({'intf_path_cost':intf['state']['cost']}) %}{% endif %} + {% if vars.update({'intf_role':intf['state']['role']}) %}{% endif %} + {% if vars.update({'intf_state':intf['state']['port-state']| replace('openconfig-spanning-tree-types:',"")}) %}{% endif %} + {% if vars.update({'intf_desig_cost':intf['state']['designated-cost']}) %}{% endif %} + {% if vars.update({'intf_desig_bridge':intf['state']['designated-bridge-address']}) %}{% endif %} + {%endif%} + {% if stp_intf['config']['bpdu-filter'] == true %} + {% if vars.update({'intf_bpdu_filter': "Y"}) %}{% endif %} + {%else %} + {% if vars.update({'intf_bpdu_filter': "N"}) %}{% endif %} + {%endif %} + {% if stp_intf['config']['link-type'] == "P2P" %} + {% if vars.update({'intf_p2pmac': "Y"}) %}{% endif %} + {%else %} + {% if vars.update({'intf_p2pmac': "N"}) %}{% endif %} + {%endif %} + {% if stp_intf['config']['edge-port'] == "EDGE_ENABLE" %} + {% if vars.update({'intf_edge': "Y"}) %}{% endif %} + {%else %} + {% if vars.update({'intf_edge': "N"}) %}{% endif %} + {%endif %} +{{'%-16s'|format(vars.intf_name)}}{{'%-5s'|format(vars.intf_pri)}}{{'%-10s'|format(vars.intf_path_cost)}}{{'%-5s'|format(vars.intf_p2pmac)}}{{'%-7s'|format(vars.intf_edge)}}{{'%-7s'|format(vars.intf_bpdu_filter)}}{{'%-10s'|format(vars.intf_role)}}{{'%-11s'|format(vars.intf_state)}}{{'%-11s'|format(vars.intf_desig_cost)}}{{'%-17s'|format(vars.intf_desig_bridge)}} + {%endif %} + {%endfor %} + {% endfor %} +{% endfor %} +{% endif %} +{% endif %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_stp_bpdu_guard.j2 b/src/CLI/renderer/templates/show_stp_bpdu_guard.j2 new file mode 100755 index 0000000000..e3cef7ee35 --- /dev/null +++ b/src/CLI/renderer/templates/show_stp_bpdu_guard.j2 @@ -0,0 +1,30 @@ +{% set vars = {'intf_name':""} %} +{% set vars = {'intf_shut_conf':""} %} +{% set vars = {'intf_shut':""} %} +{{'%-17s'|format("PortNum")}}{{'%-12s'|format("Shutdown")}}{{'%-19s'|format("Port shut")}} +{{'%-17s'|format("")}}{{'%-12s'|format("Configured")}}{{'%-19s'|format("due to BPDU guard")}} +{% if json_output -%} +{% set stp_intfs = {} %} +{% if "openconfig-spanning-tree:interfaces" is in json_output and json_output['openconfig-spanning-tree:interfaces'] is not none %} +{% set stp_intfs = json_output['openconfig-spanning-tree:interfaces']['interface'] %} +{%endif%} +{% if "openconfig-spanning-tree:interface" is in json_output and json_output['openconfig-spanning-tree:interface'] is not none %} +{% set stp_intfs = json_output['openconfig-spanning-tree:interface'] %} +{%endif%} +{% for intf in stp_intfs %} + {% if vars.update({'intf_name':""}) %}{% endif %} + {% if vars.update({'intf_shut_conf':"N"}) %}{% endif %} + {% if vars.update({'intf_shut':"NA"}) %}{% endif %} + + {% if vars.update({'intf_name':intf['name']}) %}{% endif %} + {% if (intf['config']['openconfig-spanning-tree-ext:bpdu-guard-port-shutdown'] == true) %} + {% if vars.update({'intf_shut_conf': "Y"}) %}{% endif %} + {% if (intf['state']['openconfig-spanning-tree-ext:bpdu-guard-shutdown'] == true) %} + {% if vars.update({'intf_shut': "Y"}) %}{% endif %} + {% else %} + {% if vars.update({'intf_shut': "N"}) %}{% endif %} + {% endif %} + {% endif %} +{{'%-17s'|format(vars.intf_name)}}{{'%-12s'|format(vars.intf_shut_conf)}}{{'%-19s'|format(vars.intf_shut)}} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_stp_counters.j2 b/src/CLI/renderer/templates/show_stp_counters.j2 new file mode 100755 index 0000000000..c96e460dc3 --- /dev/null +++ b/src/CLI/renderer/templates/show_stp_counters.j2 @@ -0,0 +1,83 @@ +{% set vars = {'stp_instance': ""} %} +{% set vars = {'vlan_name': ""} %} +{% set vars = {'intf_name': ""} %} +{% set vars = {'bpdu_tx': ""} %} +{% set vars = {'bpdu_rx': ""} %} +{% set vars = {'tcn_tx': ""} %} +{% set vars = {'tcn_rx': ""} %} +{% set vars = {'conf_bpdu_tx': ""} %} +{% set vars = {'conf_bpdu_rx': ""} %} +{% if json_output -%} +{% if json_output is iterable %} +{% if "openconfig-spanning-tree:enabled-protocol" is in json_output %} +{% set stp_protocol = json_output['openconfig-spanning-tree:enabled-protocol'][0]%} +{%endif%} +{% set vlan_list = {} %} +{% if stp_protocol == "openconfig-spanning-tree-ext:PVST" %} +{% set mode ="STP" %} +{% if "openconfig-spanning-tree-ext:pvst" is in json_output %} +{% set vlan_list = json_output['openconfig-spanning-tree-ext:pvst']['vlan'] %} +{% endif %} +{% if "openconfig-spanning-tree-ext:vlan" is in json_output %} +{% set vlan_list = json_output['openconfig-spanning-tree-ext:vlan'] %} +{% endif %} +{% for vlan in vlan_list %} + {% if vars.update({'vlan_name':vlan['vlan-id']}) %}{% endif %} + {% if vars.update({'stp_instance':vlan['state']['stp-instance']}) %}{% endif %} +{{" " }} +VLAN {{'%s'| format(vars.vlan_name)}} - {{mode}} instance {{'%s'| format(vars.stp_instance)}} +---------------------------------------------------------------------- +{{'%-16s'|format("PortNum")}}{{'%-10s'|format("BPDU Tx")}}{{'%-10s'|format("BPDU Rx")}}{{'%-10s'|format("TCN Tx")}}{{'%-10s'|format("TCN Rx")}} +{{'%-16s'|format("")}}{{'%-10s'|format(" ")}}{{'%-10s'|format(" ")}}{{'%-10s'|format(" ")}}{{'%-10s'|format(" ")}} +---------------------------------------------------------------------- +{% set interface_list = {} %} +{% if "interfaces" in vlan and "interface" in vlan['interfaces'] %} + {% set interface_list = vlan['interfaces']['interface'] %} +{%endif%} + {% for intf in interface_list %} + {% if vars.update({'intf_name':intf['name']}) %}{% endif %} + {% if "counters" in intf['state'] %} + {% if vars.update({'bpdu_tx':intf['state']['counters']['bpdu-sent']}) %}{% endif %} + {% if vars.update({'bpdu_rx':intf['state']['counters']['bpdu-received']}) %}{% endif %} + {% if vars.update({'tcn_rx': intf['state']['counters']['tcn-received']}) %}{% endif %} + {% if vars.update({'tcn_tx': intf['state']['counters']['tcn-sent']}) %}{% endif %} + {% endif %} +{{'%-16s'|format(vars.intf_name)}}{{'%-10s'|format(vars.bpdu_tx)}}{{'%-10s'|format(vars.bpdu_rx)}}{{'%-10s'|format(vars.tcn_tx)}}{{'%-10s'|format(vars.tcn_rx)}} + {% endfor %} +{% endfor %} +{% endif %} +{% if stp_protocol == "openconfig-spanning-tree-types:RAPID_PVST" %} +{% set mode ="RSTP" %} +{% if "openconfig-spanning-tree:rapid-pvst" is in json_output %} +{% set vlan_list = json_output['openconfig-spanning-tree:rapid-pvst']['vlan'] %} +{%endif%} +{% if "openconfig-spanning-tree:vlan" is in json_output %} +{% set vlan_list = json_output['openconfig-spanning-tree:vlan'] %} +{%endif%} +{% for vlan in vlan_list %} + {% if vars.update({'vlan_name':vlan['vlan-id']}) %}{% endif %} + {% if vars.update({'stp_instance':vlan['state']['openconfig-spanning-tree-ext:stp-instance']}) %}{% endif %} +{{" " }} +VLAN {{'%s'| format(vars.vlan_name)}} - {{mode}} instance {{'%s'| format(vars.stp_instance)}} +-------------------------------------------------------------------------------- +{{'%-16s'|format("PortNum")}}{{'%-10s'|format("BPDU Tx")}}{{'%-10s'|format("BPDU Rx")}}{{'%-12s'|format("Config BPDU")}}{{'%-12s'|format("Config BPDU")}} +{{'%-16s'|format("")}}{{'%-10s'|format(" ")}}{{'%-10s'|format(" ")}}{{'%-12s'|format("Tx")}}{{'%-12s'|format("Rx")}} +-------------------------------------------------------------------------------- +{% set interface_list = {} %} +{% if "interfaces" in vlan and "interface" in vlan['interfaces'] %} + {% set interface_list = vlan['interfaces']['interface'] %} +{%endif%} + {% for intf in interface_list %} + {% if vars.update({'intf_name':intf['name']}) %}{% endif %} + {% if "counters" in intf['state'] %} + {% if vars.update({'bpdu_tx':intf['state']['counters']['bpdu-sent']}) %}{% endif %} + {% if vars.update({'bpdu_rx':intf['state']['counters']['bpdu-received']}) %}{% endif %} + {% if vars.update({'tcn_rx': intf['state']['counters']['openconfig-spanning-tree-ext:tcn-received']}) %}{% endif %} + {% if vars.update({'tcn_tx': intf['state']['counters']['openconfig-spanning-tree-ext:tcn-sent']}) %}{% endif %} + {% endif %} +{{'%-16s'|format(vars.intf_name)}}{{'%-10s'|format(vars.bpdu_tx)}}{{'%-10s'|format(vars.bpdu_rx)}}{{'%-10s'|format(vars.config_bpdu_rx)}}{{'%-10s'|format(vars.config_bpdu_tx)}} + {% endfor %} +{% endfor %} +{%endif%} +{% endif %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_stp_root_guard.j2 b/src/CLI/renderer/templates/show_stp_root_guard.j2 new file mode 100755 index 0000000000..5a393362a8 --- /dev/null +++ b/src/CLI/renderer/templates/show_stp_root_guard.j2 @@ -0,0 +1,57 @@ +{% set vars = {'vlan_name': ""} %} +{% set vars = {'intf_name': ""} %} +{% if json_output -%} +{% if json_output is iterable %} +{% if "openconfig-spanning-tree:enabled-protocol" is in json_output %} +{% set stp_protocol = json_output['openconfig-spanning-tree:enabled-protocol'][0]%} +{%endif%} +{% set vlan_list = {} %} +{% if stp_protocol == "openconfig-spanning-tree-ext:PVST" %} +{% if "openconfig-spanning-tree-ext:pvst" is in json_output and json_output['openconfig-spanning-tree-ext:pvst'] is not none%} +{% set vlan_list = json_output['openconfig-spanning-tree-ext:pvst']['vlan'] %} +{% endif %} +{% if "openconfig-spanning-tree-ext:vlan" is in json_output and json_output['openconfig-spanning-tree-ext:vlan'] is not none %} +{% set vlan_list = json_output['openconfig-spanning-tree-ext:vlan'] %} +{% endif %} +{% endif %} +{% if stp_protocol == "openconfig-spanning-tree-types:RAPID_PVST" %} +{% if "openconfig-spanning-tree:rapid-pvst" is in json_output and json_output['openconfig-spanning-tree:rapid-pvst'] is not none %} +{% set vlan_list = json_output['openconfig-spanning-tree:rapid-pvst']['vlan'] %} +{%endif%} +{% if "openconfig-spanning-tree:vlan" is in json_output and json_ouput['openconfig-spanning-tree:vlan'] is not none %} +{% set vlan_list = json_output['openconfig-spanning-tree:vlan'] %} +{%endif%} +{%endif%} +{% if "openconfig-spanning-tree:config" in json_output %} +{% set timeout = json_output['openconfig-spanning-tree:config']['openconfig-spanning-tree-ext:rootguard-timeout'] %} +{% endif %} +Root guard timeout: {{'%-3s'|format(timeout)}} secs +{{" "}} +---------------------------------------------------------------------- +{{'%-17s'|format("PortNum")}}{{'%-6s'|format("VLAN")}}{{'%-45s'|format("Inconsistency State")}} +---------------------------------------------------------------------- +{% for vlan in vlan_list %} +{% if vars.update({'vlan_name':vlan['vlan-id']}) %}{% endif %} +{% set interface_list = {} %} +{% if "interfaces" in vlan and "interface" in vlan['interfaces'] %} + {% set interface_list = vlan['interfaces']['interface'] %} +{%endif%} + {% for intf in interface_list %} + {% if vars.update({'intf_name':intf['name']}) %}{% endif %} + {% if "state" in intf %} + {% if stp_protocol == "openconfig-spanning-tree-ext:PVST" %} + {% set root_gtime = intf['state']['root-guard-timer'] %} + {%endif %} + {% if stp_protocol == "openconfig-spanning-tree:RAPID_PVST" %} + {% set root_gtime = intf['state']['openconfig-spanning-tree-ext:root-guard-timer'] %} + {%endif %} + {% set root_gtime_string = "(" + root_gtime |string() + " seconds left on timer)"%} + {% if root_gtime > 0 %} +{{'%-17s'|format(vars.intf_name)}}{{'%-6s'|format(vars.vlan_name)}}{{'%-18s'|format("Root Inconsistent")}}{{'%-26s'|format(root_gtime_string)}} + {% endif %} + {% endif %} + {% endfor %} +{% endfor %} +{{" "}} +{% endif %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_udld_global.j2 b/src/CLI/renderer/templates/show_udld_global.j2 new file mode 100644 index 0000000000..766f5bcac3 --- /dev/null +++ b/src/CLI/renderer/templates/show_udld_global.j2 @@ -0,0 +1,19 @@ +{% if json_output -%} +{% if json_output.__len__() > 0 and json_output[0].__len__() > 1 %} +{{' '}} +{{'UDLD Global Information'}} +{% set udld_info = json_output[0] %} +{% if udld_info['admin_enable'] == True %} +{{'%-20s'|format(" Admin State")}} : UDLD Enabled +{%else %} +{{'%-20s'|format(" Admin State")}} : UDLD Disabled +{%endif%} +{% if udld_info['aggressive'] == True %} +{{'%-20s'|format(" Mode")}} : Aggressive +{%else %} +{{'%-20s'|format(" Mode")}} : Normal +{%endif%} +{{'%-20s'|format(" UDLD Message Time")}} : {{udld_info['msg_time']}} seconds +{{'%-20s'|format(" UDLD Multiplier")}} : {{udld_info['multiplier']}} +{%endif%} +{%endif%} diff --git a/src/CLI/renderer/templates/show_udld_interface.j2 b/src/CLI/renderer/templates/show_udld_interface.j2 new file mode 100644 index 0000000000..8ab8e86e9d --- /dev/null +++ b/src/CLI/renderer/templates/show_udld_interface.j2 @@ -0,0 +1,61 @@ +{% if json_output -%} +{% if json_output.__len__() > 0 %} +{% set port_config = json_output['port_config'] %} +{% if port_config and port_config.__len__() > 1 %} +{{' '}} +{{'UDLD information for'}} {{json_output['interface']}} +{% if port_config['admin_enable'] == True %} +{{'%-20s'|format(" UDLD Admin State")}} : Enabled +{%else %} +{{'%-20s'|format(" UDLD Admin State")}} : Disabled +{%endif%} +{% if port_config['aggressive'] == True %} +{{'%-20s'|format(" Mode")}} : Aggressive +{%else %} +{{'%-20s'|format(" Mode")}} : Normal +{%endif%} + +{# Mock CLI code to be removed later #} +{{'%-20s'|format(" Status")}} : {{'Bidirectional'}} +{{'%-20s'|format(" Local Device Id")}} : {{'3c2c.992d.8201'}} +{{'%-20s'|format(" Local Port Id")}} : {{port_config['ifname']}} +{{'%-20s'|format(" Local Device Name")}} : {{'Sonic'}} +{{'%-20s'|format(" Message Time")}} : {{json_output['msg_time']}} seconds +{{'%-20s'|format(" Timeout Interval")}} : {{'3'}} seconds + +{{'%-20s'|format(" Neighbor Entry")}} {{'1'}} +{{' --------------------------------------------------'}} +{{'%-30s'|format(" Neighbor Device Id")}} : {{'3c2c.992d.8235'}} +{{'%-30s'|format(" Neighbor Port Id")}} : {{'Ethernet0'}} +{{'%-30s'|format(" Neighbor Device Name")}} : {{'Sonic'}} +{{'%-30s'|format(" Neighbor Message Time")}} : {{'1'}} seconds +{{'%-30s'|format(" Neighbor Timeout Interval")}} : {{'3'}} seconds + + +{# Actual code to be enabled once App DB tables are ready #} +{# +{{'%-20s'|format(" Status")}} : {{json_output['status']}} +{% set global_oper = json_output['global_oper'] %} +{% if global_oper and global_oper.__len__() > 1 %} +{{'%-20s'|format(" Local Device Id")}} : {{global_oper['device_id']}} +{{'%-20s'|format(" Local Port Id")}} : {{port_config['ifname']}} +{{'%-20s'|format(" Local Device Name")}} : {{global_oper['device_name']}} +{{'%-20s'|format(" Message Time")}} : {{json_output['msg_time']}} seconds +{{'%-20s'|format(" Timeout Interval")}} : {{global_oper['timeout_interval']}} seconds +{%endif%} + +{% set intf_info = json_output['neighbor'] %} +{% if intf_info and intf_info.__len__() > 0 %} +{{'%-20s'|format(" Neighbor Entry")}} {{intf_info['index']}} +{{' --------------------------------------------------'}} +{{'%-30s'|format(" Neighbor Device Id")}} : {{intf_info['device_id']}} +{{'%-30s'|format(" Neighbor Port Id")}} : {{intf_info['port_id']}} +{{'%-30s'|format(" Neighbor Device Name")}} : {{intf_info['device_name']}} +{{'%-30s'|format(" Neighbor Message Time")}} : {{intf_info['msg_time']}} seconds +{{'%-30s'|format(" Neighbor Timeout Interval")}} : {{intf_info['timeout_interval']}} seconds +{%endif%} +#} + +{%endif%} +{%endif%} +{%endif%} diff --git a/src/CLI/renderer/templates/show_udld_neighbor.j2 b/src/CLI/renderer/templates/show_udld_neighbor.j2 new file mode 100644 index 0000000000..b5f7239954 --- /dev/null +++ b/src/CLI/renderer/templates/show_udld_neighbor.j2 @@ -0,0 +1,18 @@ +{% if json_output -%} +{% if json_output.__len__() > 0 %} +{{' '}} +{{'%-14s'|format("Port")}} {{'%-20s'|format("Device Name")}} {{'%-17s'|format("Device ID")}} {{'%-15s'|format("Port ID")}} {{'Neighbor State'}} +{{'--------------------------------------------------------------------------------------'}} +{# Mock CLI code to be removed later #} +{{'%-14s'|format("Ethernet12")}} {{'%-20s'|format("Sonic")}} {{'%-17s'|format("3c2c.992d.8218")}} {{'%-15s'|format("Ethernet8")}} {{'Bidirectional'}} +{{'%-14s'|format("Ethernet3")}} {{'%-20s'|format("Sonic")}} {{'%-17s'|format("3c2c.992d.8203")}} {{'%-15s'|format("Ethernet16")}} {{'Bidirectional'}} + +{# Actual code to be enabled once App DB tables are ready #} +{# +{% for i in range(json_output.__len__()) %} +{% set neigh_info = json_output[i] %} +{{'%-14s'|format(neigh_info['ifname'])}} {{'%-20s'|format(neigh_info['device_name'])}} {{'%-17s'|format(neigh_info['device_id'])}} {{'%-15s'|format(neigh_info['port_id'])}} {{neigh_info['status']}} +{% endfor %} +#} +{%endif%} +{%endif%} diff --git a/src/CLI/renderer/templates/show_udld_stats.j2 b/src/CLI/renderer/templates/show_udld_stats.j2 new file mode 100644 index 0000000000..ab639fbe4a --- /dev/null +++ b/src/CLI/renderer/templates/show_udld_stats.j2 @@ -0,0 +1,21 @@ +{% if json_output -%} +{% if json_output.__len__() > 0 %} +{{' '}} +{# Mock CLI code to be removed later #} +{{'UDLD Interface statistics for'}} {{'Ethernet0'}} +{{'%-18s'|format("Frames Transmitted")}} : {{'10'}} +{{'%-18s'|format("Frames Received")}} : {{'9'}} +{{'%-18s'|format("Frames with Error")}} : {{'0'}} + +{# Actual code to be enabled once App DB tables are ready #} +{# +{% for i in range(json_output.__len__()) %} +{% set intf_stats_info = json_output[i] %} +{{'UDLD Interface statistics for'}} {{intf_stats_info['ifname']}} +{{'%-18s'|format("Frames Transmitted")}} : {{intf_stats_info['pdu_sent']}} +{{'%-18s'|format("Frames Received")}} : {{intf_stats_info['pdu_received']}} +{{'%-18s'|format("Frames with Error")}} : {{intf_stats_info['pdu_recv_error']}} +{% endfor %} +#} +{%endif%} +{%endif%} diff --git a/src/CLI/renderer/templates/show_vlan.j2 b/src/CLI/renderer/templates/show_vlan.j2 new file mode 100644 index 0000000000..a0ea7e7283 --- /dev/null +++ b/src/CLI/renderer/templates/show_vlan.j2 @@ -0,0 +1,40 @@ +{% set vars = {'vlanName': ""} %} +{% set vars = {'ifName': ""} %} +{% set vars = {'tagMode': ""} %} +{% set vars = {'tagLetter': ""} %} +{% set vars = {'status': ""} %} +{% set vars = {'statusWord': ""} %} +{% if json_output -%} +Q: A - Access (Untagged), T - Tagged +{{'%-11s'|format("NUM")}}{{'%-12s'|format("Status")}}{{'%-7s'|format("Q Ports")}} +{% for vlanKey, vlanInfo in json_output.items() %} +{% if vars.update({'vlanName':vlanKey}) %}{% endif %} +{% if 'oper_status' in vlanInfo %} +{% if vars.update({'status':vlanInfo.oper_status}) %}{% endif %} +{% endif %} +{% if vars.status == 'up' %} +{% if vars.update({'statusWord':" Active "}) %}{% endif %} +{% else %} +{% if vars.update({'statusWord':" Inactive "}) %}{% endif %} +{% endif %} +{% if vlanInfo.vlanMembers is defined and vlanInfo.vlanMembers|length > 0 %} +{% for ifKey, ifMode in vlanInfo.vlanMembers.items() %} +{% if vars.update({'ifName':ifKey}) %}{% endif %} +{% if vars.update({'tagMode':ifMode}) %}{% endif %} +{% if ifMode == "untagged" %} +{% if vars.update({'tagLetter':" U "}) %}{% endif %} +{% else %} +{% if vars.update({'tagLetter':" T "}) %}{% endif %} +{% endif %} +{{'%-10s'|format(vars.vlanName)}}{{'%-12s'|format(vars.statusWord)}}{{'%-4s'|format(vars.tagLetter)}}{{'%-11s'|format(vars.ifName)}} +{% if vars.update({'vlanName':''}) %}{% endif %} +{% if vars.update({'statusWord':""}) %}{% endif %} +{% endfor %} +{% else %} +{{'%-10s'|format(vars.vlanName)}}{{'%-12s'|format(vars.statusWord)}} +{% if vars.update({'vlanName':''}) %}{% endif %} +{% if vars.update({'statusWord':""}) %}{% endif %} +{% endif %} +{% endfor %} +{% endif %} + diff --git a/src/CLI/renderer/templates/show_vrf.j2 b/src/CLI/renderer/templates/show_vrf.j2 new file mode 100644 index 0000000000..2c090503d0 --- /dev/null +++ b/src/CLI/renderer/templates/show_vrf.j2 @@ -0,0 +1,13 @@ +{{'----------------------------------------------------------------'}} +{{'VRF-NAME'.ljust(20)}}{{'INTERFACES'}} +{% if json_output and 'openconfig-network-instance' in json_output -%} +{% set inst_info = json_output['openconfig-network-instance'] %} +{% set state = inst_info['state'] %} +{% if state['name'] == 'mgmt' %} +{% if state['type'] == 'L3VRF' %} +{% if state['enabled'] == true %} +{{ state['name'] }} +{% endif %} +{% endif %} +{% endif %} +{% endif %} diff --git a/src/CLI/renderer/templates/show_ztp.j2 b/src/CLI/renderer/templates/show_ztp.j2 new file mode 100644 index 0000000000..b0e2a072c8 --- /dev/null +++ b/src/CLI/renderer/templates/show_ztp.j2 @@ -0,0 +1,38 @@ +{{'========================================'}} +{{'ZTP'}} +{{'========================================'}} +{% if 'ZTP Admin Mode' in json_output %} +{{'ZTP Admin Mode'.ljust(20)}}:{{json_output['ZTP Admin Mode']}} +{% endif %} +{% if 'ZTP Service' in json_output %} +{{'ZTP Service'.ljust(20)}}:{{json_output['ZTP Service']}} +{% endif %} +{% if 'ZTP Status' in json_output %} +{{'ZTP Status'.ljust(20)}}:{{json_output['ZTP Status']}} +{% endif %} +{% if 'ZTP Source' in json_output %} +{{'ZTP Source'.ljust(20)}}:{{json_output['ZTP Source']}} +{% endif %} +{% if 'Runtime' in json_output %} +{{'Runtime'.ljust(20)}}:{{json_output['Runtime']}} +{% endif %} +{% if 'Timestamp' in json_output %} +{{'Timestamp'.ljust(20)}}:{{json_output['Timestamp']}} +{% endif %} +{% if 'ZTP JSON Version' in json_output %} +{{'ZTP JSON Version'.ljust(20)}}:{{json_output['ZTP JSON Version']}} +{% endif %} +{% if 'activity-string' in json_output %} +{{json_output['activity']}} +{% endif %} +{% if 'config-list' in json_output %} +{% set c_dict = json_output['config-list'] %} +{% for key,val in c_dict.items() %} +{{'---------------------------------------------'}} +{{key.ljust(20)}} +{{'---------------------------------------------'}} +{% for k,v in val.items() %} +{{k.ljust(20)}}:{{v}} +{% endfor %} +{% endfor %} +{% endif %} diff --git a/src/CLI/renderer/templates/system_cpu_show.j2 b/src/CLI/renderer/templates/system_cpu_show.j2 new file mode 100644 index 0000000000..61028dbd45 --- /dev/null +++ b/src/CLI/renderer/templates/system_cpu_show.j2 @@ -0,0 +1,12 @@ +{{'----------------------------------------------------------------------'}} +{{'CPU'.ljust(20)}}{{'%KERNEL'.ljust(20)}}{{'%USER'.ljust(20)}}{{'%IDLE'}} +{{'----------------------------------------------------------------------'}} +{% for cpu in json_output %} + {% if (cpu['index'] != '0') %} + {% set index = (cpu['index'] | string) %} + {% else %} + {% set index = (('total') | string) %} + {% endif %} +{{('CPU-'+ index).ljust(20)}} {{(cpu['state']['kernel']['instant'] | string).ljust(20)}} {{(cpu['state']['user']['instant'] | string).ljust(20)}} {{(cpu['state']['idle']['instant'] | string)}} +{% endfor %} + diff --git a/src/CLI/renderer/templates/system_processes_show.j2 b/src/CLI/renderer/templates/system_processes_show.j2 new file mode 100755 index 0000000000..c3960df150 --- /dev/null +++ b/src/CLI/renderer/templates/system_processes_show.j2 @@ -0,0 +1,9 @@ +{% set just_var = 10 %} +{{'--------------------------------------------------------------------------'}} +{{'PID'.ljust(just_var)}}{{'%CPU'.ljust(just_var)}}{#{{'CPU-TICKS-USER'.ljust(just_var)}}{{'CPU-TICKS-SYSTEM'.ljust(just_var)}}#}{{'%MEMORY'.ljust(just_var)}}{{'MEM-USAGE(Bytes)'.ljust(just_var)}}{#{{'START-TIME'.ljust(just_var)}}{{'UP-TIME'.ljust(just_var)}}#}{{'NAME'.rjust(just_var)}} +{{'--------------------------------------------------------------------------'}} +{% for process in json_output %} +{%set name = (process['state']['name'] | string).split(' ')%} +{{(process['pid'] | string).ljust(just_var)}} {{(process['state']['cpu_utilization'] | string).ljust(just_var)}} {#{{(process['state']['cpu_usage_user'] | string).ljust(just_var)}} {{(process['state']['cpu_usage_system'] | string).ljust(just_var)}}#} {{(process['state']['memory_utilization'] | string).ljust(just_var)}} {{(process['state']['memory_usage'] | string).ljust(just_var)}} {#{{(process['state']['start_time'] | string).ljust(10)}} {{(process['state']['uptime'] | string).ljust(just_var)}}#} {{name[0]}} +{% endfor %} + diff --git a/src/CLI/renderer/templates/system_show.j2 b/src/CLI/renderer/templates/system_show.j2 new file mode 100755 index 0000000000..cd18d406ad --- /dev/null +++ b/src/CLI/renderer/templates/system_show.j2 @@ -0,0 +1,7 @@ +{{'-----------------------------------------------------------'}} +{{'Attribute'.ljust(20)}} {{'Value/State'}} +{{'-----------------------------------------------------------'}} +{% for key,value in json_output.items() %} +{{key.ljust(20)}}:{{value}} +{% endfor %} + diff --git a/src/cvl/Makefile b/src/cvl/Makefile new file mode 100644 index 0000000000..8a131838fa --- /dev/null +++ b/src/cvl/Makefile @@ -0,0 +1,83 @@ +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ + +all: precheck deps schema tests +GO?=/usr/local/go/bin/go +SRC_FILES=$(shell find . -name '*.go' | grep -v '_test.go' | grep -v '/tests/') +TEST_FILES=$(wildcard *_test.go) +TOP_DIR := $(abspath ../..) +GOFLAGS:= +BUILD_DIR:=build +GO_DOWNLOAD_PATH:=$(BUILD_GOPATH) +CVL_PKG=$(TOP_DIR)/pkg/linux_amd64/cvl.a + +CVL_TEST_DIR = $(TOP_DIR)/build/tests/cvl +CVL_TEST_BIN = $(CVL_TEST_DIR)/cvl.test + +ifdef DEBUG + GOFLAGS += -gcflags="all=-N -l" +endif + +precheck: + $(shell mkdir -p $(BUILD_DIR)) + +deps: $(BUILD_DIR)/.deps $(CVL_PKG) $(CVL_TEST_BIN) + + +$(BUILD_DIR)/.deps: + touch $@ + +$(CVL_PKG): + @echo "Building $@" + GOPATH=$(GOPATH) $(GO) build -v $(GOFLAGS) cvl + GOPATH=$(GOPATH) $(GO) install cvl + +$(CVL_TEST_BIN): $(TEST_FILES) $(SRC_FILES) + GOPATH=$(GOPATH) $(GO) test -c -cover -coverpkg=cvl,cvl/internal/util,cvl/internal/yparser cvl -o $@ + cp -r testdata $(@D)/ + +install: + GOPATH=$(GO_DEP_PATH) $(GO) install + +schema: + make -C schema + +tests: + make -C tests + +gotest: + make -C schema + make -C testdata/schema + cp schema/*.yin testdata/schema + rm -rf testdata/schema/platform + cp -rf schema/platform testdata/schema/ + CVL_CFG_FILE=$(abspath .)/conf/cvl_cfg.json CVL_SCHEMA_PATH=$(abspath .)/testdata/schema GOPATH=$(GOPATH) tests/run_test.sh + +clean: + make -C tests clean + make -C schema clean + make -C testdata/schema clean + rm -rf $(CVL_PKG) + rm -rf $(CVL_TEST_DIR) + +cleanall:clean + rm -rf $(BUILD_DIR) + rm -rf $(CVL_PKG) + rm -rf $(CVL_TEST_DIR) + diff --git a/src/cvl/README.md b/src/cvl/README.md new file mode 100644 index 0000000000..77ed20dcc6 --- /dev/null +++ b/src/cvl/README.md @@ -0,0 +1,70 @@ +1. Install latest version of pyang tool. + +2. Install libyang from https://github.com/CESNET/libyang along with its dependency. + +3. Run 'make' from top level 'cvl' directory. + +4. Refer to top level makefile rules for compiling individual targets. + +5. 'schema' directory should contain all .yin files + +6. On the target the 'schema' directory needs to be present in the same directory where application executable file is present. + + +Debugging Info: +=============== + +Below steps need to be done to enable CVL logging. + +1. Find the CVL json config file in mgmt-framework docker in switch at "/usr/sbin/cvl_cfg.json" . + +2. Change the logging flags from "false" to "true" as below: + +``` + { + "TRACE_CACHE": "true", + "TRACE_LIBYANG": "true", + "TRACE_YPARSER": "true", + "TRACE_CREATE": "true", + "TRACE_UPDATE": "true", + "TRACE_DELETE": "true", + "TRACE_SEMANTIC": "true", + "TRACE_SYNTAX": "true", + "TRACE_ONERROR": "true", + "__comment1__": "Set LOGTOSTDER to 'true' to log on standard error", + "LOGTOSTDERR": "false", + "__comment2__": "Log messages to standard error at or above this severity level", + "STDERRTHRESHOLD": "ERROR", + "__comment3__": "Log to /tmp/cvl.log file", + "LOG_TO_FILE": "true", + "__comment4__": "Limit log file size in bytes, 0 means no limit, default 10MB", + "LOG_FILE_SIZE": "10485760", + "__comment5__": "Set verbosity level(1 to 8) for verbose logs", + "VERBOSITY": "0", + "SKIP_VALIDATION": "false", + "SKIP_SEMANTIC_VALIDATION": "false" + } +``` + +3. Below environment variables need to be set at the end in /usr/bin/rest-server.sh in mgmt-framework docker. + + export CVL_DEBUG=1 + export CVL_CFG_FILE=/usr/sbin/cvl_cfg.json + + Note : CVL_CFG_FILE enviroment variable can point to other location also. + +4. CVL Traces can be enabled both with restart and without mgmt-framework docker restart . + + With Restart: + ============ + Restart mgmt-framework docker after which updated cvl_cfg.json file will be read. + + Without Restart: + =============== + Issue SIGUSR2 to rest process(kill -SIGUSR2 , to read changed cvl_cfg.json with logging enabled. + +5. After following above steps, CVL traces can be seen in syslog file in host container at /var/log/syslog. + +6. To disable CVL traces , disable the fields in cvl_cfg.json file and then perform same steps as in Step 4. + + diff --git a/src/cvl/conf/cvl_cfg.json b/src/cvl/conf/cvl_cfg.json new file mode 100644 index 0000000000..fbfef629cb --- /dev/null +++ b/src/cvl/conf/cvl_cfg.json @@ -0,0 +1,23 @@ +{ + "TRACE_CACHE": "true", + "TRACE_LIBYANG": "true", + "TRACE_YPARSER": "true", + "TRACE_CREATE": "true", + "TRACE_UPDATE": "true", + "TRACE_DELETE": "true", + "TRACE_SEMANTIC": "true", + "TRACE_SYNTAX": "true", + "TRACE_ONERROR": "true", + "__comment1__": "Set LOGTOSTDER to 'true' to log on standard error", + "LOGTOSTDERR": "false", + "__comment2__": "Display log upto INFO level", + "STDERRTHRESHOLD": "ERROR", + "__comment3__": "Log to /tmp/cvl.log file", + "LOG_TO_FILE": "true", + "__comment4__": "Limit log file size in bytes, 0 means no limit, default 10MB", + "LOG_FILE_SIZE": "10485760", + "__comment5__": "Display log upto INFO level 8", + "VERBOSITY": "0", + "SKIP_VALIDATION": "false", + "SKIP_SEMANTIC_VALIDATION": "false" +} diff --git a/src/cvl/custom_validation/common.go b/src/cvl/custom_validation/common.go new file mode 100644 index 0000000000..0844c399d3 --- /dev/null +++ b/src/cvl/custom_validation/common.go @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 custom_validation + +import ( + "fmt" + "reflect" + "github.com/antchfx/xmlquery" + "github.com/go-redis/redis" + . "cvl/internal/util" + "cvl/internal/yparser" + ) + +type CustomValidation struct {} + +type CVLValidateType uint +const ( + VALIDATE_NONE CVLValidateType = iota //Data is used as dependent data + VALIDATE_SYNTAX //Syntax is checked and data is used as dependent data + VALIDATE_SEMANTICS //Semantics is checked + VALIDATE_ALL //Syntax and Semantics are checked +) + +type CVLOperation uint +const ( + OP_NONE CVLOperation = 0 //Used to just validate the config without any operation + OP_CREATE = 1 << 0//For Create operation + OP_UPDATE = 1 << 1//For Update operation + OP_DELETE = 1 << 2//For Delete operation +) + +//Error code +type CVLRetCode int +const ( + CVL_SUCCESS CVLRetCode = iota + CVL_ERROR + CVL_NOT_IMPLEMENTED + CVL_INTERNAL_UNKNOWN + CVL_FAILURE + CVL_SYNTAX_ERROR = CVLRetCode(yparser.YP_SYNTAX_ERROR) + CVL_SEMANTIC_ERROR = CVLRetCode(yparser.YP_SEMANTIC_ERROR) + CVL_SYNTAX_MISSING_FIELD = CVLRetCode(yparser.YP_SYNTAX_MISSING_FIELD) + CVL_SYNTAX_INVALID_FIELD = CVLRetCode(yparser.YP_SYNTAX_INVALID_FIELD) /* Invalid Field */ + CVL_SYNTAX_INVALID_INPUT_DATA = CVLRetCode(yparser.YP_SYNTAX_INVALID_INPUT_DATA) /*Invalid Input Data */ + CVL_SYNTAX_MULTIPLE_INSTANCE = CVLRetCode(yparser.YP_SYNTAX_MULTIPLE_INSTANCE) /* Multiple Field Instances */ + CVL_SYNTAX_DUPLICATE = CVLRetCode(yparser.YP_SYNTAX_DUPLICATE) /* Duplicate Fields */ + CVL_SYNTAX_ENUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_ENUM_INVALID) /* Invalid enum value */ + CVL_SYNTAX_ENUM_INVALID_NAME = CVLRetCode(yparser.YP_SYNTAX_ENUM_INVALID_NAME) /* Invalid enum name */ + CVL_SYNTAX_ENUM_WHITESPACE = CVLRetCode(yparser.YP_SYNTAX_ENUM_WHITESPACE) /* Enum name with leading/trailing whitespaces */ + CVL_SYNTAX_OUT_OF_RANGE = CVLRetCode(yparser.YP_SYNTAX_OUT_OF_RANGE) /* Value out of range/length/pattern (data) */ + CVL_SYNTAX_MINIMUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_MINIMUM_INVALID) /* min-elements constraint not honored */ + CVL_SYNTAX_MAXIMUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_MAXIMUM_INVALID) /* max-elements constraint not honored */ + CVL_SEMANTIC_DEPENDENT_DATA_MISSING = CVLRetCode(yparser.YP_SEMANTIC_DEPENDENT_DATA_MISSING) /* Dependent Data is missing */ + CVL_SEMANTIC_MANDATORY_DATA_MISSING = CVLRetCode(yparser.YP_SEMANTIC_MANDATORY_DATA_MISSING) /* Mandatory Data is missing */ + CVL_SEMANTIC_KEY_ALREADY_EXIST = CVLRetCode(yparser.YP_SEMANTIC_KEY_ALREADY_EXIST) /* Key already existing. */ + CVL_SEMANTIC_KEY_NOT_EXIST = CVLRetCode(yparser.YP_SEMANTIC_KEY_NOT_EXIST) /* Key is missing. */ + CVL_SEMANTIC_KEY_DUPLICATE = CVLRetCode(yparser.YP_SEMANTIC_KEY_DUPLICATE) /* Duplicate key. */ + CVL_SEMANTIC_KEY_INVALID = CVLRetCode(yparser.YP_SEMANTIC_KEY_INVALID) +) + +//Strcture for key and data in API +type CVLEditConfigData struct { + VType CVLValidateType //Validation type + VOp CVLOperation //Operation type + Key string //Key format : "PORT|Ethernet4" + Data map[string]string //Value : {"alias": "40GE0/28", "mtu" : 9100, "admin_status": down} +} + +/* CVL Error Structure. */ +type CVLErrorInfo struct { + TableName string /* Table having error */ + ErrCode CVLRetCode /* CVL Error return Code. */ + CVLErrDetails string /* CVL Error Message details. */ + Keys []string /* Keys of the Table having error. */ + Value string /* Field Value throwing error */ + Field string /* Field Name throwing error . */ + Msg string /* Detailed error message. */ + ConstraintErrMsg string /* Constraint error message. */ + ErrAppTag string +} + +//Custom validation context passed to custom validation function +type CustValidationCtxt struct { + ReqData []CVLEditConfigData //All request data + CurCfg *CVLEditConfigData //Current request data for which validation should be done + YNodeName string //YANG node name + YNodeVal string //YANG node value, leaf-list will have "," separated value + YCur *xmlquery.Node //YANG data tree + RClient *redis.Client //Redis client +} + +//Common function to invoke custom validation +//TBD should we do this using GO plugin feature ? +func InvokeCustomValidation(cv *CustomValidation, name string, args... interface{}) CVLErrorInfo { + inputs := make([]reflect.Value, len(args)) + for i, _ := range args { + inputs[i] = reflect.ValueOf(args[i]) + } + + f := reflect.ValueOf(cv).MethodByName(name) + if (f.IsNil() == false) { + v := f.Call(inputs) + TRACE_LEVEL_LOG(TRACE_SEMANTIC, + "InvokeCustomValidation: %s(), return value = %v", v[0]) + + fmt.Printf("Return value = %v\n", v[0]) + return (v[0].Interface()).(CVLErrorInfo) + } + + return CVLErrorInfo{ErrCode: CVL_SUCCESS} +} + diff --git a/src/cvl/custom_validation/custom_validation.go b/src/cvl/custom_validation/custom_validation.go new file mode 100644 index 0000000000..a22794b603 --- /dev/null +++ b/src/cvl/custom_validation/custom_validation.go @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 custom_validation + +import ( + "github.com/go-redis/redis" + "strings" + ) + +//Custom validation code for sonic-acl.yang// +///////////////////////////////////////////// +//Path : /sonic-acl/ACL_TABLE/ACL_TABLE_LIST +//Purpose: Allow maximum 1024 ACL tables +//vc : Custom Validation Context +//Returns - CVL Error object +const MAX_ACL_TABLE_INSTANCES = 1024 +func (t *CustomValidation) ValidateMaxAclTable( + vc *CustValidationCtxt) CVLErrorInfo { + + var nokey []string + ls := redis.NewScript(`return #redis.call('KEYS', "ACL_TABLE|*")`) + + //Get current coutnt from Redis + redisEntries, err := ls.Run(vc.RClient, nokey).Result() + if err != nil { + return CVLErrorInfo{ErrCode: CVL_SEMANTIC_ERROR} + } + + aclTblCount := int(redisEntries.(int64)) + //Get count from user request + for idx := 0; idx < len(vc.ReqData); idx++ { + if (vc.ReqData[idx].VOp == OP_CREATE) && + (strings.HasPrefix(vc.ReqData[idx].Key, "ACL_TABLE|")) { + aclTblCount = aclTblCount + 1 + } + } + + if (aclTblCount > MAX_ACL_TABLE_INSTANCES) { + return CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "ACL_TABLE", + CVLErrDetails : "Max table count exceeded", + } + } + + return CVLErrorInfo{ErrCode: CVL_SUCCESS} +} + +//Path : /sonic-acl/ACL_RULE/ACL_RULE_LIST/IP_TYPE +//Purpose: Check correct for IP address provided +// based on type IP_TYPE +//vc : Custom Validation Context +//Returns - CVL Error object +func (t *CustomValidation) ValidateAclRuleIPAddress( + vc *CustValidationCtxt) CVLErrorInfo { + + if (vc.YNodeVal == "") { + return CVLErrorInfo{ErrCode: CVL_SUCCESS} + } + + if (vc.YNodeVal == "ANY" || vc.YNodeVal == "IP" || + vc.YNodeVal == "IPV4" || vc.YNodeVal == "IPV4ANY") { + + _, srcIpV4exists := vc.CurCfg.Data["SRC_IP"] + _, dstIpV4exists := vc.CurCfg.Data["DST_IP"] + + if (srcIpV4exists == false) || (dstIpV4exists == false) { + return CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "ACL_RULE", + CVLErrDetails : "IP address is missing for " + + "IP_TYPE=" + vc.YNodeVal, + } + } + + } else if (vc.YNodeVal == "ANY" || vc.YNodeVal == "IP" || + vc.YNodeVal == "IPV6" || vc.YNodeVal == "IPV6ANY") { + + _, srcIpV6exists := vc.CurCfg.Data["SRC_IPV6"] + _, dstIpV6exists := vc.CurCfg.Data["DST_IPV6"] + + if (srcIpV6exists == false) || (dstIpV6exists == false) { + return CVLErrorInfo{ + ErrCode: CVL_SEMANTIC_ERROR, + TableName: "ACL_RULE", + CVLErrDetails : "IP address is missing for " + + "IP_TYPE=" + vc.YNodeVal, + } + } + } + + return CVLErrorInfo{ErrCode: CVL_SUCCESS} +} + diff --git a/src/cvl/cvl.go b/src/cvl/cvl.go new file mode 100644 index 0000000000..c12e61f150 --- /dev/null +++ b/src/cvl/cvl.go @@ -0,0 +1,1391 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package cvl +import ( + "fmt" + "os" + "strings" + "regexp" + "time" + "github.com/go-redis/redis" + "github.com/antchfx/xmlquery" + "github.com/antchfx/xpath" + "github.com/antchfx/jsonquery" + "cvl/internal/yparser" + . "cvl/internal/util" + "sync" + "flag" + "io/ioutil" + "path/filepath" + custv "cvl/custom_validation" + "unsafe" +) + +//DB number +const ( + APPL_DB uint8 = 0 + iota + ASIC_DB + COUNTERS_DB + LOGLEVEL_DB + CONFIG_DB + PFC_WD_DB + FLEX_COUNTER_DB = PFC_WD_DB + STATE_DB + SNMP_OVERLAY_DB + INVALID_DB +) + +const DEFAULT_CACHE_DURATION uint16 = 300 /* 300 sec */ +const MAX_BULK_ENTRIES_IN_PIPELINE int = 50 +const MAX_DEVICE_METADATA_FETCH_RETRY = 60 +const PLATFORM_SCHEMA_PATH = "platform/" + +var reLeafRef *regexp.Regexp = nil +var reHashRef *regexp.Regexp = nil +var reSelKeyVal *regexp.Regexp = nil +var reLeafInXpath *regexp.Regexp = nil + +var cvlInitialized bool +var dbNameToDbNum map[string]uint8 + +//map of lua script loaded +var luaScripts map[string]*redis.Script + +type tblFieldPair struct { + tableName string + field string +} + +type xpathExpression struct { + expr string + exprTree *xpath.Expr + errCode string + errStr string +} + +//var tmpDbCache map[string]interface{} //map of table storing map of key-value pair + //m["PORT_TABLE] = {"key" : {"f1": "v1"}} +//Important schema information to be loaded at bootup time +type modelTableInfo struct { + dbNum uint8 + modelName string + redisTableName string //To which Redis table it belongs to, used for 1 Redis to N Yang List + module *yparser.YParserModule + keys []string + redisKeyDelim string + redisKeyPattern string + redisTableSize int + mapLeaf []string //for 'mapping list' + leafRef map[string][]string //for storing all leafrefs for a leaf in a table, + //multiple leafref possible for union + xpathExpr map[string]*xpathExpression + tablesForMustExp map[string]CVLOperation + refFromTables []tblFieldPair //list of table or table/field referring to this table + custValidation map[string]string // Map for custom validation node and function name +} + + +/* CVL Error Structure. */ +type CVLErrorInfo struct { + TableName string /* Table having error */ + ErrCode CVLRetCode /* CVL Error return Code. */ + CVLErrDetails string /* CVL Error Message details. */ + Keys []string /* Keys of the Table having error. */ + Value string /* Field Value throwing error */ + Field string /* Field Name throwing error . */ + Msg string /* Detailed error message. */ + ConstraintErrMsg string /* Constraint error message. */ + ErrAppTag string +} + +// Struct for request data and YANG data +type requestCacheType struct { + reqData CVLEditConfigData + yangData *xmlquery.Node +} + +// Struct for CVL session +type CVL struct { + redisClient *redis.Client + yp *yparser.YParser + tmpDbCache map[string]interface{} //map of table storing map of key-value pair + requestCache map[string]map[string][]*requestCacheType//Cache of validated data, + //per table, per key. Can be used as dependent data in next request + batchLeaf string + chkLeafRefWithOthCache bool + yv *YValidator //Custom YANG validator for validating external dependencies +} + +// Struct for model namepsace and prefix +type modelNamespace struct { + prefix string + ns string +} + +// Struct for storing all YANG list schema info +type modelDataInfo struct { + modelNs map[string]*modelNamespace //model namespace + tableInfo map[string]*modelTableInfo //redis table to model name and keys + redisTableToYangList map[string][]string //Redis table to all YANG lists when it is not 1:1 mapping + allKeyDelims map[string]bool +} + +//Struct for storing global DB cache to store DB which are needed frequently like PORT +type dbCachedData struct { + root *yparser.YParserNode //Root of the cached data + startTime time.Time //When cache started + expiry uint16 //How long cache should be maintained in sec +} + +//Global data cache for redis table +type cvlGlobalSessionType struct { + db map[string]dbCachedData + pubsub *redis.PubSub + stopChan chan int //stop channel to stop notification listener + cv *CVL + mutex *sync.Mutex +} + +// Struct for storing key and value pair +type keyValuePairStruct struct { + key string + values []string +} + +var cvg cvlGlobalSessionType + +//Single redis client for validation +var redisClient *redis.Client + +//Stores important model info +var modelInfo modelDataInfo + +func TRACE_LOG(tracelevel CVLTraceLevel, fmtStr string, args ...interface{}) { + TRACE_LEVEL_LOG(tracelevel, fmtStr, args...) +} + +func CVL_LOG(level CVLLogLevel, fmtStr string, args ...interface{}) { + CVL_LEVEL_LOG(level, fmtStr, args...) +} + +//package init function +func init() { + if (os.Getenv("CVL_SCHEMA_PATH") != "") { + CVL_SCHEMA = os.Getenv("CVL_SCHEMA_PATH") + "/" + } + + if (os.Getenv("CVL_DEBUG") != "") { + SetTrace(true) + } + + ConfigFileSyncHandler() + + cvlCfgMap := ReadConfFile() + + if (cvlCfgMap != nil) { + flag.Set("v", cvlCfgMap["VERBOSITY"]) + if (strings.Compare(cvlCfgMap["LOGTOSTDERR"], "true") == 0) { + flag.Set("logtostderr", "true") + flag.Set("stderrthreshold", cvlCfgMap["STDERRTHRESHOLD"]) + } + + CVL_LOG(INFO ,"Current Values of CVL Configuration File %v", cvlCfgMap) + } + + //regular expression for leafref and hashref finding + reLeafRef = regexp.MustCompile(`.*[/]([-_a-zA-Z]*:)?(.*)[/]([-_a-zA-Z]*:)?(.*)`) + reHashRef = regexp.MustCompile(`\[(.*)\|(.*)\]`) + //Regular expression to select key value + reSelKeyVal = regexp.MustCompile("=[ ]*['\"]?([0-9_a-zA-Z]+)['\"]?|(current[(][)])") + //Regular expression to find leafref in xpath + reLeafInXpath = regexp.MustCompile("(.*[:/]{1})([a-zA-Z0-9_-]+)([^a-zA-Z0-9_-]*)") + + if Initialize() != CVL_SUCCESS { + CVL_LOG(FATAL, "CVL initialization failed") + } + + cvg.db = make(map[string]dbCachedData) + + //Global session keeps the global cache + cvg.cv, _ = ValidationSessOpen() + //Create buffer channel of length 1 + cvg.stopChan = make(chan int, 1) + //Initialize mutex + cvg.mutex = &sync.Mutex{} + + _, err := redisClient.ConfigSet("notify-keyspace-events", "AKE").Result() + if err != nil { + CVL_LOG(ERROR ,"Could not enable notification error %s", err) + } + + //Store PORT table details in global cache and update the cache if PORT entry changes + dbCacheSet(false, "PORT", 0) +} + +func Debug(on bool) { + yparser.Debug(on) +} + +//Get attribute value of xml node +func getXmlNodeAttr(node *xmlquery.Node, attrName string) string { + for _, attr := range node.Attr { + if (attrName == attr.Name.Local) { + return attr.Value + } + } + + return "" +} + +// Load all YIN schema files, apply deviation files +func loadSchemaFiles() CVLRetCode { + + platformName := "" + // Wait to check if CONFIG_DB is populated with DEVICE_METADATA. + // This is needed to apply deviation file + retryCnt := 0 + for ; (retryCnt < MAX_DEVICE_METADATA_FETCH_RETRY); retryCnt++ { + deviceMetaDataKey, err := redisClient.Keys("DEVICE_METADATA|localhost").Result() + if (err != nil) || (len(deviceMetaDataKey) == 0) { + //Retry for 1 min + time.Sleep(100 * time.Millisecond) //sleep for 1 sec and then retry + continue + } + + //Redis is populated with DEVICE_METADATA + break + } + + //Now try to fetch the platform details + if (retryCnt < MAX_DEVICE_METADATA_FETCH_RETRY) { + deviceMetaData, err := redisClient.HGetAll("DEVICE_METADATA|localhost").Result() + var exists bool + platformName, exists = deviceMetaData["platform"] + if (err != nil) || (exists == false) || (platformName == "") { + CVL_LOG(WARNING, "Could not fetch 'platform' details from CONFIG_DB") + } + } + + //Scan schema directory to get all schema files + modelFiles, err := filepath.Glob(CVL_SCHEMA + "/*.yin") + if err != nil { + CVL_LOG(FATAL ,"Could not read schema files %v", err) + } + + moduleMap := map[string]*yparser.YParserModule{} + // Load all common schema files + for _, modelFilePath := range modelFiles { + _, modelFile := filepath.Split(modelFilePath) + + TRACE_LOG(TRACE_LIBYANG, "Parsing schema file %s ...", + modelFilePath) + + // Now parse each schema file + var module *yparser.YParserModule + if module, _ = yparser.ParseSchemaFile(modelFilePath); module == nil { + + CVL_LOG(FATAL,fmt.Sprintf("Unable to parse schema file %s", modelFile)) + return CVL_ERROR + } + + moduleMap[modelFile] = module + } + + // Load all platform specific schema files based on platform details + // present in DEVICE_METADATA + for { + if (platformName == "") { + CVL_LOG(INFO, "Skipping parsing of any platform specific YIN schema " + + "files as platform name can't be determined") + break + } + + // Read directory under 'platform' directory + allDirs, errDir := ioutil.ReadDir(CVL_SCHEMA + "/" + PLATFORM_SCHEMA_PATH) + if (errDir != nil) || (len(allDirs) == 0) { + CVL_LOG(INFO, "Could not read platform schema location or no platform " + + "specific schema exists. %v", err) + break + } + + // For matched platform directory parse all schema files + for _, sDir := range allDirs { + + sDirName := sDir.Name() + //Check which directory matches + if (strings.Contains(platformName, sDirName) == false) { + continue + } + + //Get all platform specific YIN schema file names + modelFiles, err := filepath.Glob(CVL_SCHEMA + "/" + + PLATFORM_SCHEMA_PATH + "/" + sDirName + "/*.yin") + if err != nil { + CVL_LOG(WARNING,"Could not read platform schema directory %v", err) + break + } + + + //Now parse platform schema files + for _, modelFilePath := range modelFiles { + _, modelFile := filepath.Split(modelFilePath) + + TRACE_LOG(TRACE_YPARSER, "Parsing platform specific schema" + "file %s ...\n", modelFilePath) + + var module *yparser.YParserModule + if module, _ = yparser.ParseSchemaFile(modelFilePath); module == nil { + + CVL_LOG(ERROR, "Unable to parse schema file %s", modelFile) + return CVL_ERROR + } + + moduleMap[modelFile] = module + } + + //platform found + break + } + + break + } + + for modelFile, parsedModule := range moduleMap { + //store schema related info to use in validation + storeModelInfo(modelFile, parsedModule) + } + + + //Build reverse leafref info i.e. which table/field uses one table through leafref + buildRefTableInfo() + + return CVL_SUCCESS +} + +func storeModelInfo(modelFile string, module *yparser.YParserModule) { //such model info can be maintained in C code and fetched from there + + //model is derived from file name + tokens := strings.Split(modelFile, ".") + modelName := tokens[0] + + //Store namespace and prefix + ns, prefix := yparser.GetModelNs(module) + modelInfo.modelNs[modelName] = &modelNamespace{ns, prefix} + + list := yparser.GetModelListInfo(module) + + if (list == nil) { + CVL_LOG(ERROR, "Unable to get schema details for %s", modelFile) + return + } + + + for _, lInfo := range list { + TRACE_LOG(TRACE_YPARSER, + "Storing schema details for list %s", lInfo.ListName) + + tInfo := modelTableInfo{modelName: modelName} + + tInfo.dbNum = dbNameToDbNum[lInfo.DbName] + tInfo.redisTableName = lInfo.RedisTableName + tInfo.module = module + tInfo.redisKeyDelim = lInfo.RedisKeyDelim + tInfo.redisKeyPattern = lInfo.RedisKeyPattern + tInfo.redisTableSize = lInfo.RedisTableSize + tInfo.keys = lInfo.Keys + tInfo.mapLeaf = lInfo.MapLeaf + tInfo.leafRef = lInfo.LeafRef + tInfo.custValidation = lInfo.CustValidation + tInfo.xpathExpr = make(map[string]*xpathExpression, len(lInfo.XpathExpr)) + for nodeName, xpr := range lInfo.XpathExpr { + tInfo.xpathExpr[nodeName] = &xpathExpression{ + expr: xpr.Expr, + errCode: xpr.ErrCode, + errStr: xpr.ErrStr, + } + } + + modelInfo.allKeyDelims[tInfo.redisKeyDelim] = true + yangList := modelInfo.redisTableToYangList[tInfo.redisTableName] + yangList = append(yangList, lInfo.ListName) + //Update the map + modelInfo.redisTableToYangList[tInfo.redisTableName] = yangList + + modelInfo.tableInfo[lInfo.ListName] = &tInfo + } +} + +// Get YANG list to Redis table name +func yangToRedisTblName(yangListName string) string { + if (strings.HasSuffix(yangListName, "_LIST")) { + return yangListName[0:len(yangListName) - len("_LIST")] + } + return yangListName +} + +//This functions build info of dependent table/fields +//which uses a particular table through leafref +func buildRefTableInfo() { + + CVL_LOG(INFO_API, "Building reverse reference info from leafref") + + for tblName, tblInfo := range modelInfo.tableInfo { + if (len(tblInfo.leafRef) == 0) { + continue + } + + //For each leafref update the table used through leafref + for fieldName, leafRefs := range tblInfo.leafRef { + for _, leafRef := range leafRefs { + matches := reLeafRef.FindStringSubmatch(leafRef) + + //We have the leafref table name + if (matches != nil && len(matches) == 5) { //whole + 4 sub matches + refTable := yangToRedisTblName(matches[2]) + refTblInfo := modelInfo.tableInfo[refTable] + + refFromTables := &refTblInfo.refFromTables + *refFromTables = append(*refFromTables, tblFieldPair{tblName, fieldName}) + modelInfo.tableInfo[refTable] = refTblInfo + } + } + } + + } + + //Now sort list 'refFromTables' under each table based on dependency among them + for tblName, tblInfo := range modelInfo.tableInfo { + if (len(tblInfo.refFromTables) == 0) { + continue + } + + depTableList := []string{} + for i:=0; i < len(tblInfo.refFromTables); i++ { + depTableList = append(depTableList, tblInfo.refFromTables[i].tableName) + } + + sortedTableList, _ := cvg.cv.SortDepTables(depTableList) + if (len(sortedTableList) == 0) { + continue + } + + newRefFromTables := []tblFieldPair{} + + for i:=0; i < len(sortedTableList); i++ { + //Find fieldName + fieldName := "" + for j :=0; j < len(tblInfo.refFromTables); j++ { + if (sortedTableList[i] == tblInfo.refFromTables[j].tableName) { + fieldName = tblInfo.refFromTables[j].field + break + } + } + newRefFromTables = append(newRefFromTables, tblFieldPair{sortedTableList[i], fieldName}) + } + //Update sorted refFromTables + tblInfo.refFromTables = newRefFromTables + modelInfo.tableInfo[tblName] = tblInfo + } + +} + +//Find the tables names in must expression, these tables data need to be fetched +//during semantic validation +func addTableNamesForMustExp() { + + for tblName, tblInfo := range modelInfo.tableInfo { + if (tblInfo.xpathExpr == nil) { + continue + } + + tblInfo.tablesForMustExp = make(map[string]CVLOperation) + + for _, mustExp := range tblInfo.xpathExpr { + var op CVLOperation = OP_NONE + //Check if 'must' expression should be executed for a particular operation + if (strings.Contains(mustExp.expr, + ":operation != 'CREATE'") == true) { + op = op | OP_CREATE + } else if (strings.Contains(mustExp.expr, + ":operation != 'UPDATE'") == true) { + op = op | OP_UPDATE + } else if (strings.Contains(mustExp.expr, + ":operation != 'DELETE'") == true) { + op = op | OP_DELETE + } + + //store the current table if aggregate function like count() is used + /*if (strings.Contains(mustExp, "count") == true) { + tblInfo.tablesForMustExp[tblName] = op + }*/ + + //check which table name is present in the must expression + for tblNameSrch, _ := range modelInfo.tableInfo { + if (tblNameSrch == tblName) { + continue + } + //Table name should appear like "../VLAN_MEMBER_LIST/tagging_mode' or ' + // "/prt:PORT/prt:ifname" + re := regexp.MustCompile(fmt.Sprintf(".*[/]([-_a-zA-Z]*:)?%s_LIST[\\[/]", tblNameSrch)) + matches := re.FindStringSubmatch(mustExp.expr) + if (len(matches) > 0) { + //stores the table name + tblInfo.tablesForMustExp[tblNameSrch] = op + } + } + } + + //update map + modelInfo.tableInfo[tblName] = tblInfo + } +} + +//Split key into table prefix and key +func splitRedisKey(key string) (string, string) { + + var foundIdx int = -1 + //Check with all key delim + for keyDelim, _ := range modelInfo.allKeyDelims { + foundIdx = strings.Index(key, keyDelim) + if (foundIdx >= 0) { + //Matched with key delim + break + } + } + + if (foundIdx < 0) { + //No matches + CVL_LOG(ERROR, "Could not find any of key delimeter %v in key '%s'", + modelInfo.allKeyDelims, key) + return "", "" + } + + tblName := key[:foundIdx] + + if _, exists := modelInfo.tableInfo[tblName]; exists == false { + //Wrong table name + CVL_LOG(ERROR, "Could not find table '%s' in schema", tblName) + return "", "" + } + + prefixLen := foundIdx + 1 + + + TRACE_LOG(TRACE_SYNTAX, "Split Redis Key %s into (%s, %s)", + key, tblName, key[prefixLen:]) + + return tblName, key[prefixLen:] +} + +//Get the YANG list name from Redis key and table name +//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) (yangList string) { + defer func() { + pYangList := &yangList + TRACE_LOG(TRACE_SYNTAX, "Got YANG list '%s' " + + "from Redis Table '%s', Key '%s'", *pYangList, tableName, key) + }() + + mapArr, exists := modelInfo.redisTableToYangList[tableName] + + if (exists == false) || (len(mapArr) == 1) { //no map or only one + //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{ + keyNames := modelInfo.tableInfo[tableName].keys + //First split all the keys components + keyVals := strings.Split(redisKey, modelInfo.tableInfo[tableName].redisKeyDelim) //split by DB separator + //Store patterns for each key components by splitting using key delim + keyPatterns := strings.Split(modelInfo.tableInfo[tableName].redisKeyPattern, + modelInfo.tableInfo[tableName].redisKeyDelim) //split by DB separator + + /* TBD. Workaround for optional keys in INTERFACE Table. + Code will be removed once model is finalized. */ + if ((tableName == "INTERFACE") && (len(keyNames) != len(keyVals))) { + keyVals = append(keyVals, "0.0.0.0/0") + + } else if (len(keyNames) != len(keyVals)) { + return nil //number key names and values does not match + } + + mkeys := []keyValuePairStruct{} + //For each key check the pattern and store key/value pair accordingly + for idx, keyName := range keyNames { + + //check if key-pattern contains specific key pattern + if (keyPatterns[idx+1] == fmt.Sprintf("{%s}", keyName)) { //no specific key pattern - just "{key}" + //Store key/value mapping + mkeys = append(mkeys, keyValuePairStruct{keyName, []string{keyVals[idx]}}) + } else if (keyPatterns[idx+1] == fmt.Sprintf("({%s},)*", keyName)) { // key pattern is "({key},)*" i.e. repeating keys seperated by ',' + repeatedKeys := strings.Split(keyVals[idx], ",") + mkeys = append(mkeys, keyValuePairStruct{keyName, repeatedKeys}) + } + } + + TRACE_LOG(TRACE_SYNTAX, "getRedisToYangKeys() returns %v " + + "from Redis Table '%s', Key '%s'", mkeys, tableName, redisKey) + + return mkeys +} + +//Check for path resolution. Find Redis entry to be fetched from the path. +func (c *CVL) checkPathForTableEntry(tableName string, currentValue string, cfgData *CVLEditConfigData, mustExpStk []string, token string) ([]string, string, CVLRetCode) { + + n := len(mustExpStk) - 1 + xpath := "" + + if (token == ")") { + for n = len(mustExpStk) - 1; mustExpStk[n] != "("; n = len(mustExpStk) - 1 { + //pop until "(" + xpath = mustExpStk[n] + xpath + mustExpStk = mustExpStk[:n] + } + } else if (token == "]") { + //pop until "[" + for n = len(mustExpStk) - 1; mustExpStk[n] != "["; n = len(mustExpStk) - 1 { + xpath = mustExpStk[n] + xpath + mustExpStk = mustExpStk[:n] + } + } + + mustExpStk = mustExpStk[:n] + targetTbl := "" + //Search the table name in xpath + for tblNameSrch, _ := range modelInfo.tableInfo { + if (tblNameSrch == tableName) { + continue + } + //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_LIST", tblNameSrch)) //no preifx + if (tblSrchIdx < 0) { + tblSrchIdx = strings.Index(xpath, fmt.Sprintf(":%s_LIST", tblNameSrch)) //with prefix + } + if (tblSrchIdx < 0) { + continue + } + + tblSrchIdxEnd := strings.Index(xpath[tblSrchIdx+len(tblNameSrch)+1:], "[") + if (tblSrchIdxEnd < 0) { + tblSrchIdxEnd = strings.Index(xpath[tblSrchIdx+len(tblNameSrch)+1:], "/") + } + + if (tblSrchIdxEnd >= 0) { //match found + targetTbl = tblNameSrch + break + } + } + + //No match with table found, could be just keys like 'aclname='TestACL1' + //just return the same + if (targetTbl == "") { + return mustExpStk, xpath, CVL_SUCCESS + } + + tableKey := targetTbl + //Add the keys + keyNames := modelInfo.tableInfo[tableKey].keys + + //Form the Redis Key to fetch the entry + for idx, keyName := range keyNames { + //Key value is string/numeric literal, extract the same + keySrchIdx := strings.Index(xpath, keyName) + if (keySrchIdx < 0 ) { + continue + } + + matches := reSelKeyVal.FindStringSubmatch(xpath[keySrchIdx+len(keyName):]) + if (len(matches) > 1) { + if (matches[1] == "current()") { + //replace with current field value + tableKey = tableKey + "*" + modelInfo.tableInfo[tableName].redisKeyDelim + currentValue + } else { + //Use literal + tableKey = tableKey + "*" + modelInfo.tableInfo[tableName].redisKeyDelim + matches[1] + } + + if (idx != len(keyNames) - 1) { + tableKey = tableKey + "|*" + } + } + } + + //Fetch the entries + redisTableKeys, err:= redisClient.Keys(tableKey).Result() + + if (err !=nil || len (redisTableKeys) > 1) { //more than one entry is returned, can't proceed further + //Just add all the entries for caching + for _, redisTableKey := range redisTableKeys { + c.addTableEntryToCache(splitRedisKey(redisTableKey)) + } + + return mustExpStk, "", CVL_SUCCESS + } + + for _, redisTableKey := range redisTableKeys { + + var entry map[string]string + + if tmpEntry, mergeNeeded := c.fetchDataFromRequestCache(splitRedisKey(redisTableKey)); (tmpEntry == nil || mergeNeeded == true) { + //If data is not available in validated cache fetch from Redis DB + entry, err = redisClient.HGetAll(redisTableKey).Result() + + if (mergeNeeded) { + mergeMap(entry, tmpEntry) + } + } + + //Get the entry fields from Redis + if (entry != nil) { + //Just add all the entries for caching + c.addTableEntryToCache(splitRedisKey(redisTableKey)) + + leafInPath := "" + index := strings.LastIndex(xpath, "/") + if (index >= 0) { + + matches := reLeafInXpath.FindStringSubmatch(xpath[index:]) + if (len(matches) > 2) { //should return atleasts two subgroup and entire match + leafInPath = matches[2] + } else { + //No leaf requested in xpath selection + return mustExpStk, "", CVL_SUCCESS + } + + index = strings.Index(xpath, "=") + tblIndex := strings.Index(xpath, targetTbl) + if (index >= 0 && tblIndex >=0) { + + if leafVal, existing := entry[leafInPath + "@"]; existing == true { + //Get the field value referred in the xpath + if (index < tblIndex) { + // case like - [ifname=../../ACL_TABLE[aclname=current()] + return mustExpStk, xpath[:index+1] + leafVal, CVL_SUCCESS + } else { + // case like - [ifname=current()] + return mustExpStk, leafVal, CVL_SUCCESS + } + } else { + + if (index < tblIndex) { + return mustExpStk, xpath[:index+1] + entry[leafInPath], CVL_SUCCESS + } else { + return mustExpStk, entry[leafInPath], CVL_SUCCESS + } + } + } + } + } + } + + return mustExpStk, "", CVL_FAILURE +} + +//Add specific entries by looking at must expression +//Must expression may need single or multiple entries +//It can be within same table or across multiple tables +//Node-set function such count() can be quite expensive and +//should be avoided through this function +func (c *CVL) addTableEntryForMustExp(cfgData *CVLEditConfigData, tableName string) CVLRetCode { + if (modelInfo.tableInfo[tableName].xpathExpr == nil) { + return CVL_SUCCESS + } + + for fieldName, mustExp := range modelInfo.tableInfo[tableName].xpathExpr { + + currentValue := "" // Current value for current() function + + //Get the current() field value from the entry being created/updated/deleted + keyValuePair := getRedisToYangKeys(tableName, cfgData.Key[len(tableName)+1:]) + + //Try to get the current() from the 'key' provided + if (keyValuePair != nil) { + for _, keyValItem := range keyValuePair { + if (keyValItem.key == fieldName) { + currentValue = keyValItem.values[0] + } + } + } + + TRACE_LOG(TRACE_CACHE, "Attempting adding data for must expression %s", + mustExp.expr) + + //current() value needs to be fetched from other field + if (currentValue == "") { + if (cfgData.VOp == OP_CREATE) { + if (tableName != fieldName) { //must expression is not at list level + currentValue = cfgData.Data[fieldName] + if (currentValue == "") { + currentValue = cfgData.Data[fieldName + "@"] + } + } + } else if (cfgData.VOp == OP_UPDATE || cfgData.VOp == OP_DELETE) { + //fetch the entry to get current() value + c.clearTmpDbCache() + entryKey := cfgData.Key[len(tableName)+1:] + c.tmpDbCache[tableName] = map[string]interface{}{entryKey: nil} + + if (c.fetchTableDataToTmpCache(tableName, + map[string]interface{}{entryKey: nil}) > 0) { + mapTable := c.tmpDbCache[tableName] + if fields, existing := mapTable.(map[string]interface{})[entryKey]; existing == true { + currentValue = fmt.Sprintf("%v", fields.(map[string]interface{})[fieldName]) + } + } + } + } + + mustExpStk := []string{} //Use the string slice as stack + mustExpStr := "(" + mustExp.expr + ")" + strLen := len(mustExpStr) + strTmp := "" + //Parse the xpath expression and fetch Redis entry by looking at xpath, + // any xpath function call is ignored except current(). + for i := 0; i < strLen; i++ { + switch mustExpStr[i] { + case '(': + if (mustExpStr[i+1] == ')') { + strTmp = strTmp + "()" + if index := strings.Index(strTmp, "current()"); index >= 0 { + strTmp = strTmp[:index] + currentValue + } + i = i + 1 + continue + } + if (strTmp != "") { + mustExpStk = append(mustExpStk, strTmp) + } + mustExpStk = append(mustExpStk, "(") + strTmp = "" + case ')': + if (strTmp != "") { + mustExpStk = append(mustExpStk, strTmp) + } + strTmp = "" + //Check Path - pop until ')' + mustExpStk, evalPath,_ := c.checkPathForTableEntry(tableName, currentValue, cfgData, + mustExpStk, ")") + if (evalPath != "") { + mustExpStk = append(mustExpStk, evalPath) + } + mustExpStk = append(mustExpStk, ")") + case '[': + if (strTmp != "") { + mustExpStk = append(mustExpStk, strTmp) + } + mustExpStk = append(mustExpStk, "[") + strTmp = "" + case ']': + if (strTmp != "") { + mustExpStk = append(mustExpStk, strTmp) + } + //Check Path - pop until = or '[' + mustExpStk, evalPath,_ := c.checkPathForTableEntry(tableName, currentValue, cfgData, + mustExpStk, "]") + if (evalPath != "") { + mustExpStk = append(mustExpStk, "[" + evalPath + "]") + } + strTmp = "" + default: + strTmp = fmt.Sprintf("%s%c", strTmp, mustExpStr[i]) + } + } + + //Get the redis data for accumulated keys and add them to session cache + depData := c.fetchDataToTmpCache() //fetch data to temp cache for temporary validation + + if (depData != nil) { + if (Tracing == true) { + TRACE_LOG(TRACE_CACHE, "Adding entries for 'must' expression : %s", c.yp.NodeDump(depData)) + } + } else { + CVL_LOG(WARNING, "Could not fetch entries for 'must' expression : %s", mustExp.expr) + //Could not fetch any entry from Redis after xpath evaluation + return CVL_FAILURE + } + + if errObj := c.yp.CacheSubtree(false, depData); errObj.ErrCode != yparser.YP_SUCCESS { + CVL_LOG(WARNING, "Unable to add dependent data to session cache " + + " for 'must' expression : %s", mustExp.expr) + return CVL_FAILURE + } + + } //for each must expression + + return CVL_SUCCESS +} + +//Add all other table data for validating all 'must' exp for tableName +func (c *CVL) addTableDataForMustExp(op CVLOperation, tableName string) CVLRetCode { + if (modelInfo.tableInfo[tableName].xpathExpr == nil) { + return CVL_SUCCESS + } + + for mustTblName, mustOp := range modelInfo.tableInfo[tableName].tablesForMustExp { + //First check if must expression should be executed for the given operation + if (mustOp != OP_NONE) && ((mustOp & op) == OP_NONE) { + //must to be excuted for particular operation, but current operation + //is not the same one + continue + } + + //Check in global cache first and merge to session cache + if topNode, _ := dbCacheGet(mustTblName); topNode != nil { + var errObj yparser.YParserError + //If global cache has the table, add to the session validation + TRACE_LOG(TRACE_CACHE, "Adding global cache to session cache for table %s", tableName) + if errObj = c.yp.CacheSubtree(true, topNode); errObj.ErrCode != yparser.YP_SUCCESS { + return CVL_SYNTAX_ERROR + } + } else { //Put the must table in global table and add to session cache + cvg.cv.chkLeafRefWithOthCache = true + dbCacheSet(false, mustTblName, 100*DEFAULT_CACHE_DURATION) //Keep the cache for default duration + cvg.cv.chkLeafRefWithOthCache = false + + if topNode, ret := dbCacheGet(mustTblName); topNode != nil { + var errObj yparser.YParserError + //If global cache has the table, add to the session validation + TRACE_LOG(TRACE_CACHE, "Global cache created, add the data to session cache for table %s", tableName) + if errObj = c.yp.CacheSubtree(true, topNode); errObj.ErrCode != yparser.YP_SUCCESS { + CVL_LOG(WARNING, "Unable to add dependent data from " + + "global to session cache for table %s", mustTblName) + return CVL_SYNTAX_ERROR + } + } else if (ret == CVL_SUCCESS) { + TRACE_LOG(TRACE_CACHE, "Global cache empty, no data in Redis for table %s", tableName) + return CVL_SUCCESS + } else { + CVL_LOG(ERROR ,"Could not create global cache for table %s", mustTblName) + return CVL_ERROR + } + + + /* + tableKeys, err:= redisClient.Keys(mustTblName + + modelInfo.tableInfo[mustTblName].redisKeyDelim + "*").Result() + + if (err != nil) { + continue + } + + for _, tableKey := range tableKeys { + tableKey = tableKey[len(mustTblName+ modelInfo.tableInfo[mustTblName].redisKeyDelim):] //remove table prefix + if (c.tmpDbCache[mustTblName] == nil) { + c.tmpDbCache[mustTblName] = map[string]interface{}{tableKey: nil} + } else { + tblMap := c.tmpDbCache[mustTblName] + tblMap.(map[string]interface{})[tableKey] =nil + c.tmpDbCache[mustTblName] = tblMap + } + } + */ + } + } + + return CVL_SUCCESS +} + +//Add leafref entry for caching +//It has to be recursive in nature, as there can be chained leafref +func (c *CVL) addLeafRef(config bool, tableName string, name string, value string) { + + if (config == false) { + return + } + + //Check if leafRef entry is there for this field + if (len(modelInfo.tableInfo[tableName].leafRef[name]) > 0) { //array of leafrefs for a leaf + for _, leafRef := range modelInfo.tableInfo[tableName].leafRef[name] { + + //Get reference table name from the path and the leaf name + matches := reLeafRef.FindStringSubmatch(leafRef) + + //We have the leafref table name and the leaf name as well + if (matches != nil && len(matches) == 5) { //whole + 4 sub matches + refTableName := matches[2] + redisKey := value + + //Check if leafref dependency can also be met from 'must' table + if (c.chkLeafRefWithOthCache == true) { + found := false + for mustTbl, _ := range modelInfo.tableInfo[tableName].tablesForMustExp { + if mustTbl == refTableName { + found = true + break + } + } + if (found == true) { + //Leafref data will be available from must table dep data, skip this leafref entry + continue + } + } + + //only key is there, value wil be fetched and stored here, + //if value can't fetched this entry will be deleted that time + //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[refRedisTableName] + _, exist := tblMap.(map[string]interface{})[redisKey] + if (exist == false) { + tblMap.(map[string]interface{})[redisKey] = nil + c.tmpDbCache[refRedisTableName] = tblMap + } + } + } + } + } +} + +//Checks field map values and removes "NULL" entry, create array for leaf-list +func (c *CVL) checkFieldMap(fieldMap *map[string]string) map[string]interface{} { + fieldMapNew := map[string]interface{}{} + + for field, value := range *fieldMap { + if (field == "NULL") { + continue + } else if (field[len(field)-1:] == "@") { + //last char @ means it is a leaf-list/array of fields + field = field[:len(field)-1] //strip @ + //split the values seprated using ',' + strArr := strings.Split(value, ",") + //fieldMapNew[field] = strings.Split(value, ",") + arrMap := make([]interface{}, 0)//len(strArr)) + for _, strArrItem := range strArr { + arrMap = append(arrMap, strArrItem) + } + fieldMapNew[field] = arrMap//make([]interface{}, len(strArr)) + } else { + fieldMapNew[field] = value + } + } + + return fieldMapNew +} + +//Merge 'src' map to 'dest' map of map[string]string type +func mergeMap(dest map[string]string, src map[string]string) { + TRACE_LOG(TRACE_SEMANTIC, + "Merging map %v into %v", src, dest) + + for key, data := range src { + dest[key] = data + } +} + +func (c *CVL) translateToYang(jsonMap *map[string]interface{}) (*yparser.YParserNode, CVLErrorInfo) { + + var cvlErrObj CVLErrorInfo + //Parse the map data to json tree + data, _ := jsonquery.ParseJsonMap(jsonMap) + var root *yparser.YParserNode + root = nil + var errObj yparser.YParserError + + for jsonNode := data.FirstChild; jsonNode != nil; jsonNode=jsonNode.NextSibling { + TRACE_LOG(TRACE_LIBYANG, "Translating, Top Node=%v\n", jsonNode.Data) + //Visit each top level list in a loop for creating table data + topNode, cvlErrObj := c.generateTableData(true, jsonNode) + + //Generate YANG data for Yang Validator + topYangNode, cvlYErrObj := c.generateYangListData(jsonNode, true) + + if topNode == nil { + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + CVL_LOG(ERROR, "Unable to translate request data to YANG format") + return nil, cvlErrObj + } + + if topYangNode == nil { + cvlYErrObj.ErrCode = CVL_SYNTAX_ERROR + CVL_LOG(ERROR, "Unable to translate request data to YANG format") + return nil, cvlYErrObj + } + + if (root == nil) { + root = topNode + } else { + if root, errObj = c.yp.MergeSubtree(root, topNode); errObj.ErrCode != yparser.YP_SUCCESS { + CVL_LOG(ERROR, "Unable to merge translated YANG data(libyang) " + + "while translating from request data to YANG format") + return nil, cvlErrObj + } + } + + //Create a full document and merge with main YANG data + doc := &xmlquery.Node{Type: xmlquery.DocumentNode} + doc.FirstChild = topYangNode + doc.LastChild = topYangNode + topYangNode.Parent = doc + if c.mergeYangData(c.yv.root, doc) != CVL_SUCCESS { + CVL_LOG(ERROR, "Unable to merge translated YANG data while " + + "translating from request data to YANG format") + cvlYErrObj.ErrCode = CVL_SYNTAX_ERROR + return nil, cvlErrObj + } + } + + return root, cvlErrObj +} + +//Validate config - syntax and semantics +func (c *CVL) validate (data *yparser.YParserNode) CVLRetCode { + + depData := c.fetchDataToTmpCache() + /* + if (depData != nil) { + if (0 != C.lyd_merge_to_ctx(&data, depData, C.LYD_OPT_DESTRUCT, ctx)) { + TRACE_LOG(1, "Failed to merge status data\n") + } + } + + if (0 != C.lyd_data_validate(&data, C.LYD_OPT_CONFIG, ctx)) { + fmt.Println("Validation failed\n") + return CVL_SYNTAX_ERROR + }*/ + + TRACE_LOG(TRACE_LIBYANG, "\nValidate1 data=%v\n", c.yp.NodeDump(data)) + errObj := c.yp.ValidateData(data, depData) + if yparser.YP_SUCCESS != errObj.ErrCode { + return CVL_FAILURE + } + + return CVL_SUCCESS +} + +func createCVLErrObj(errObj yparser.YParserError) CVLErrorInfo { + + cvlErrObj := CVLErrorInfo { + TableName : errObj.TableName, + ErrCode : CVLRetCode(errObj.ErrCode), + CVLErrDetails : cvlErrorMap[CVLRetCode(errObj.ErrCode)], + Keys : errObj.Keys, + Value : errObj.Value, + Field : errObj.Field, + Msg : errObj.Msg, + ConstraintErrMsg : errObj.ErrTxt, + ErrAppTag : errObj.ErrAppTag, + } + + + return cvlErrObj + +} + +//Perform syntax checks +func (c *CVL) validateSyntax(data *yparser.YParserNode) (CVLErrorInfo, CVLRetCode) { + var cvlErrObj CVLErrorInfo + TRACE_LOG(TRACE_YPARSER, "Validating syntax ....") + + if errObj := c.yp.ValidateSyntax(data); errObj.ErrCode != yparser.YP_SUCCESS { + + retCode := CVLRetCode(errObj.ErrCode) + + cvlErrObj = CVLErrorInfo { + TableName : errObj.TableName, + ErrCode : CVLRetCode(errObj.ErrCode), + CVLErrDetails : cvlErrorMap[retCode], + Keys : errObj.Keys, + Value : errObj.Value, + Field : errObj.Field, + Msg : errObj.Msg, + ConstraintErrMsg : errObj.ErrTxt, + ErrAppTag : errObj.ErrAppTag, + } + + CVL_LOG(ERROR,"Syntax validation failed. Error - %v", cvlErrObj) + + return cvlErrObj, retCode + } + + return cvlErrObj, CVL_SUCCESS +} + +//Perform semantic checks +func (c *CVL) validateSemantics(data *yparser.YParserNode, appDepData *yparser.YParserNode) (CVLErrorInfo, CVLRetCode) { + var cvlErrObj CVLErrorInfo + + if (SkipSemanticValidation() == true) { + return cvlErrObj, CVL_SUCCESS + } + + //Get dependent data from + depData := c.fetchDataToTmpCache() //fetch data to temp cache for temporary validation + + if (Tracing == true) { + TRACE_LOG(TRACE_SEMANTIC, "Validating semantics data=%s\n depData =%s\n, appDepData=%s\n....", c.yp.NodeDump(data), c.yp.NodeDump(depData), c.yp.NodeDump(appDepData)) + } + + if errObj := c.yp.ValidateSemantics(data, depData, appDepData); errObj.ErrCode != yparser.YP_SUCCESS { + + retCode := CVLRetCode(errObj.ErrCode) + + cvlErrObj = CVLErrorInfo { + TableName : errObj.TableName, + ErrCode : CVLRetCode(errObj.ErrCode), + CVLErrDetails : cvlErrorMap[retCode], + Keys : errObj.Keys, + Value : errObj.Value, + Field : errObj.Field, + Msg : errObj.Msg, + ConstraintErrMsg : errObj.ErrTxt, + ErrAppTag : errObj.ErrAppTag, + } + + CVL_LOG(ERROR,"Semantic validation failed. Error - %v", cvlErrObj) + + return cvlErrObj, retCode + } + + return cvlErrObj ,CVL_SUCCESS +} + +//Add config data item to accumulate per table +func (c *CVL) addCfgDataItem(configData *map[string]interface{}, + cfgDataItem CVLEditConfigData) (string, string){ + var cfgData map[string]interface{} + cfgData = *configData + + tblName, key := splitRedisKey(cfgDataItem.Key) + if (tblName == "" || key == "") { + //Bad redis key + return "", "" + } + + if (cfgDataItem.VOp == OP_DELETE) { + //Don't add data it is delete operation + return tblName, key + } + + if _, existing := cfgData[tblName]; existing { + fieldsMap := cfgData[tblName].(map[string]interface{}) + fieldsMap[key] = c.checkFieldMap(&cfgDataItem.Data) + } else { + fieldsMap := make(map[string]interface{}) + fieldsMap[key] = c.checkFieldMap(&cfgDataItem.Data) + cfgData[tblName] = fieldsMap + } + + return tblName, key +} + +//Perform user defined custom validation +func (c *CVL) doCustomValidation(custvCfg []custv.CVLEditConfigData, + curCustvCfg *custv.CVLEditConfigData, + tbl, key string) CVLErrorInfo { + + cvlErrObj := CVLErrorInfo{ErrCode : CVL_SUCCESS} + + for nodeName, custFunc := range modelInfo.tableInfo[tbl].custValidation { + //check in requestCache for YANG data first + var node *xmlquery.Node = nil + if (c.requestCache[tbl][key][0].yangData != nil) { + node = c.requestCache[tbl][key][0].yangData + } else { + //Find the node from YANG tree + node = c.moveToYangList(tbl, key) + } + + if (node == nil) { + CVL_LOG(WARNING, "Could not find data for custom validation, " + + "node name=%s, func name =%s", nodeName, custFunc) + continue + } + + //find the node value + //node value is empty for custom validation function at list level + nodeVal := "" + if (strings.HasSuffix(nodeName, "_LIST") == false) { + for nodeLeaf := node.FirstChild; nodeLeaf != nil; + nodeLeaf = nodeLeaf.NextSibling { + if (nodeName != nodeLeaf.Data) { + continue + } + + if (len(nodeLeaf.Attr) > 0) && + (nodeLeaf.Attr[0].Name.Local == "leaf-list") { + nodeVal = curCustvCfg.Data[nodeName] + } else { + nodeVal = nodeLeaf.FirstChild.Data + } + } + + } + + //Call csutom validation functions + CVL_LOG(INFO_TRACE, "Calling custom validation function %s", custFunc) + errObj := custv.InvokeCustomValidation(&custv.CustomValidation{}, + custFunc, + &custv.CustValidationCtxt{ + ReqData: custvCfg, + CurCfg: curCustvCfg, + YNodeName: nodeName, + YNodeVal: nodeVal, + YCur: node, + RClient: redisClient}) + + cvlErrObj = *(*CVLErrorInfo)(unsafe.Pointer(&errObj)) + + if (cvlErrObj.ErrCode != CVL_SUCCESS) { + CVL_LOG(ERROR, "Custom validation failed, Error = %v", cvlErrObj) + } + } + + return cvlErrObj +} + diff --git a/src/cvl/cvl_api.go b/src/cvl/cvl_api.go new file mode 100644 index 0000000000..3d45f88560 --- /dev/null +++ b/src/cvl/cvl_api.go @@ -0,0 +1,715 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package cvl + +import ( + "fmt" + "reflect" + "encoding/json" + "github.com/go-redis/redis" + toposort "github.com/philopon/go-toposort" + "cvl/internal/yparser" + . "cvl/internal/util" + "strings" + "github.com/antchfx/xmlquery" + "unsafe" + custv "cvl/custom_validation" +) + +type CVLValidateType uint +const ( + VALIDATE_NONE CVLValidateType = iota //Data is used as dependent data + VALIDATE_SYNTAX //Syntax is checked and data is used as dependent data + VALIDATE_SEMANTICS //Semantics is checked + VALIDATE_ALL //Syntax and Semantics are checked +) + +type CVLOperation uint +const ( + OP_NONE CVLOperation = 0 //Used to just validate the config without any operation + OP_CREATE = 1 << 0//For Create operation + OP_UPDATE = 1 << 1//For Update operation + OP_DELETE = 1 << 2//For Delete operation +) + +var cvlErrorMap = map[CVLRetCode]string { + CVL_SUCCESS : "Config Validation Success", + CVL_SYNTAX_ERROR : "Config Validation Syntax Error", + CVL_SEMANTIC_ERROR : "Config Validation Semantic Error", + CVL_SYNTAX_MISSING_FIELD : "Required Field is Missing", + CVL_SYNTAX_INVALID_FIELD : "Invalid Field Received", + CVL_SYNTAX_INVALID_INPUT_DATA : "Invalid Input Data Received", + CVL_SYNTAX_MULTIPLE_INSTANCE : "Multiple Field Instances Received", + CVL_SYNTAX_DUPLICATE : "Duplicate Instances Received", + CVL_SYNTAX_ENUM_INVALID : "Invalid Enum Value Received", + CVL_SYNTAX_ENUM_INVALID_NAME : "Invalid Enum Value Received", + CVL_SYNTAX_ENUM_WHITESPACE : "Enum name with leading/trailing whitespaces Received", + CVL_SYNTAX_OUT_OF_RANGE : "Value out of range/length/pattern (data)", + CVL_SYNTAX_MINIMUM_INVALID : "min-elements constraint not honored", + CVL_SYNTAX_MAXIMUM_INVALID : "max-elements constraint not honored", + CVL_SEMANTIC_DEPENDENT_DATA_MISSING : "Dependent Data is missing", + CVL_SEMANTIC_MANDATORY_DATA_MISSING : "Mandatory Data is missing", + CVL_SEMANTIC_KEY_ALREADY_EXIST : "Key already existing.", + CVL_SEMANTIC_KEY_NOT_EXIST : "Key is missing.", + CVL_SEMANTIC_KEY_DUPLICATE : "Duplicate key received", + CVL_SEMANTIC_KEY_INVALID : "Invalid Key Received", + CVL_INTERNAL_UNKNOWN : "Internal Unknown Error", + CVL_ERROR : "Generic Error", + CVL_NOT_IMPLEMENTED : "Error Not Implemented", + CVL_FAILURE : "Generic Failure", +} + +//Error code +type CVLRetCode int +const ( + CVL_SUCCESS CVLRetCode = iota + CVL_ERROR + CVL_NOT_IMPLEMENTED + CVL_INTERNAL_UNKNOWN + CVL_FAILURE + CVL_SYNTAX_ERROR = CVLRetCode(yparser.YP_SYNTAX_ERROR) + CVL_SEMANTIC_ERROR = CVLRetCode(yparser.YP_SEMANTIC_ERROR) + CVL_SYNTAX_MISSING_FIELD = CVLRetCode(yparser.YP_SYNTAX_MISSING_FIELD) + CVL_SYNTAX_INVALID_FIELD = CVLRetCode(yparser.YP_SYNTAX_INVALID_FIELD) /* Invalid Field */ + CVL_SYNTAX_INVALID_INPUT_DATA = CVLRetCode(yparser.YP_SYNTAX_INVALID_INPUT_DATA) /*Invalid Input Data */ + CVL_SYNTAX_MULTIPLE_INSTANCE = CVLRetCode(yparser.YP_SYNTAX_MULTIPLE_INSTANCE) /* Multiple Field Instances */ + CVL_SYNTAX_DUPLICATE = CVLRetCode(yparser.YP_SYNTAX_DUPLICATE) /* Duplicate Fields */ + CVL_SYNTAX_ENUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_ENUM_INVALID) /* Invalid enum value */ + CVL_SYNTAX_ENUM_INVALID_NAME = CVLRetCode(yparser.YP_SYNTAX_ENUM_INVALID_NAME) /* Invalid enum name */ + CVL_SYNTAX_ENUM_WHITESPACE = CVLRetCode(yparser.YP_SYNTAX_ENUM_WHITESPACE) /* Enum name with leading/trailing whitespaces */ + CVL_SYNTAX_OUT_OF_RANGE = CVLRetCode(yparser.YP_SYNTAX_OUT_OF_RANGE) /* Value out of range/length/pattern (data) */ + CVL_SYNTAX_MINIMUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_MINIMUM_INVALID) /* min-elements constraint not honored */ + CVL_SYNTAX_MAXIMUM_INVALID = CVLRetCode(yparser.YP_SYNTAX_MAXIMUM_INVALID) /* max-elements constraint not honored */ + CVL_SEMANTIC_DEPENDENT_DATA_MISSING = CVLRetCode(yparser.YP_SEMANTIC_DEPENDENT_DATA_MISSING) /* Dependent Data is missing */ + CVL_SEMANTIC_MANDATORY_DATA_MISSING = CVLRetCode(yparser.YP_SEMANTIC_MANDATORY_DATA_MISSING) /* Mandatory Data is missing */ + CVL_SEMANTIC_KEY_ALREADY_EXIST = CVLRetCode(yparser.YP_SEMANTIC_KEY_ALREADY_EXIST) /* Key already existing. */ + CVL_SEMANTIC_KEY_NOT_EXIST = CVLRetCode(yparser.YP_SEMANTIC_KEY_NOT_EXIST) /* Key is missing. */ + CVL_SEMANTIC_KEY_DUPLICATE = CVLRetCode(yparser.YP_SEMANTIC_KEY_DUPLICATE) /* Duplicate key. */ + CVL_SEMANTIC_KEY_INVALID = CVLRetCode(yparser.YP_SEMANTIC_KEY_INVALID) +) + +//Strcture for key and data in API +type CVLEditConfigData struct { + VType CVLValidateType //Validation type + VOp CVLOperation //Operation type + Key string //Key format : "PORT|Ethernet4" + Data map[string]string //Value : {"alias": "40GE0/28", "mtu" : 9100, "admin_status": down} +} + +func Initialize() CVLRetCode { + if (cvlInitialized == true) { + //CVL has already been initialized + return CVL_SUCCESS + } + + //Initialize redis Client + redisClient = redis.NewClient(&redis.Options{ + Addr: ":6379", + Password: "", // no password set + DB: int(CONFIG_DB), // use APP DB + }) + + if (redisClient == nil) { + CVL_LOG(FATAL, "Unable to connect with Redis Config DB") + return CVL_ERROR + } + + //Load lua script into redis + luaScripts = make(map[string]*redis.Script) + loadLuaScript(luaScripts) + + yparser.Initialize() + + modelInfo.modelNs = make(map[string]*modelNamespace) //redis table to model name + 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} + + // Load all YIN schema files + if retCode := loadSchemaFiles(); retCode != CVL_SUCCESS { + return retCode + } + + //Add all table names to be fetched to validate 'must' expression + addTableNamesForMustExp() + + cvlInitialized = true + + return CVL_SUCCESS +} + +func Finish() { + yparser.Finish() +} + +func ValidationSessOpen() (*CVL, CVLRetCode) { + cvl := &CVL{} + cvl.tmpDbCache = make(map[string]interface{}) + cvl.requestCache = make(map[string]map[string][]*requestCacheType) + cvl.yp = &yparser.YParser{} + cvl.yv = &YValidator{} + cvl.yv.root = &xmlquery.Node{Type: xmlquery.DocumentNode} + + if (cvl == nil || cvl.yp == nil) { + return nil, CVL_FAILURE + } + + return cvl, CVL_SUCCESS +} + +func ValidationSessClose(c *CVL) CVLRetCode { + c.yp.DestroyCache() + c = nil + + return CVL_SUCCESS +} + +func (c *CVL) ValidateStartupConfig(jsonData string) CVLRetCode { + //Check config data syntax + //Finally validate + return CVL_NOT_IMPLEMENTED +} + +func (c *CVL) ValidateIncrementalConfig(jsonData string) CVLRetCode { + //Check config data syntax + //Fetch the depedent data + //Merge config and dependent data + //Finally validate + c.clearTmpDbCache() + var v interface{} + + b := []byte(jsonData) + if err := json.Unmarshal(b, &v); err != nil { + return CVL_SYNTAX_ERROR + } + + var dataMap map[string]interface{} = v.(map[string]interface{}) + + root, _ := c.translateToYang(&dataMap) + if root == nil { + return CVL_SYNTAX_ERROR + + } + + //Add and fetch entries if already exists in Redis + for tableName, data := range dataMap { + for key, _ := range data.(map[string]interface{}) { + c.addTableEntryToCache(tableName, key) + } + } + + existingData := c.fetchDataToTmpCache() + + //Merge existing data for update syntax or checking duplicate entries + var errObj yparser.YParserError + if (existingData != nil) { + if root, errObj = c.yp.MergeSubtree(root, existingData); + errObj.ErrCode != yparser.YP_SUCCESS { + return CVL_ERROR + } + } + + //Clear cache + c.clearTmpDbCache() + + //Add tables for 'must' expression + for tableName, _ := range dataMap { + c.addTableDataForMustExp(OP_NONE, tableName) + } + + //Perform validation + if _, cvlRetCode := c.validateSemantics(root, nil); cvlRetCode != CVL_SUCCESS { + return cvlRetCode + } + + return CVL_SUCCESS +} + +//Validate data for operation +func (c *CVL) ValidateConfig(jsonData string) CVLRetCode { + c.clearTmpDbCache() + var v interface{} + + b := []byte(jsonData) + if err := json.Unmarshal(b, &v); err == nil { + var value map[string]interface{} = v.(map[string]interface{}) + root, _ := c.translateToYang(&value) + //if ret == CVL_SUCCESS && root != nil { + if root == nil { + return CVL_FAILURE + + } + + if (c.validate(root) != CVL_SUCCESS) { + return CVL_FAILURE + } + + } + + return CVL_SUCCESS +} + +//Validate config data based on edit operation - no marshalling in between +func (c *CVL) ValidateEditConfig(cfgData []CVLEditConfigData) (CVLErrorInfo, CVLRetCode) { + var cvlErrObj CVLErrorInfo + + if (SkipValidation() == true) { + CVL_LOG(INFO_TRACE, "Skipping CVL validation.") + return cvlErrObj, CVL_SUCCESS + } + + //Type cast to custom validation cfg data + sliceHeader := *(*reflect.SliceHeader)(unsafe.Pointer(&cfgData)) + custvCfg := *(*[]custv.CVLEditConfigData)(unsafe.Pointer(&sliceHeader)) + + c.clearTmpDbCache() + c.yv.root.FirstChild = nil + c.yv.root.LastChild = nil + + + //Step 1: Get requested data first + //add all dependent data to be fetched from Redis + requestedData := make(map[string]interface{}) + + cfgDataLen := len(cfgData) + for i := 0; i < cfgDataLen; i++ { + if (VALIDATE_ALL != cfgData[i].VType) { + continue + } + + //Add config data item to be validated + tbl,key := c.addCfgDataItem(&requestedData, cfgData[i]) + + //Add to request cache + reqTbl, exists := c.requestCache[tbl] + if (exists == false) { + //Create new table key data + reqTbl = make(map[string][]*requestCacheType) + } + cfgDataItemArr, _ := reqTbl[key] + cfgDataItemArr = append(cfgDataItemArr, &requestCacheType{cfgData[i], nil}) + reqTbl[key] = cfgDataItemArr + c.requestCache[tbl] = reqTbl + + //Invalid table name or invalid key separator + if key == "" { + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SYNTAX_ERROR + } + + switch cfgData[i].VOp { + case OP_CREATE: + //Check max-element constraint + if ret := c.checkMaxElemConstraint(tbl); ret != CVL_SUCCESS { + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SYNTAX_ERROR + } + + if (c.addTableEntryForMustExp(&cfgData[i], tbl) != CVL_SUCCESS) { + c.addTableDataForMustExp(cfgData[i].VOp, tbl) + } + + case OP_UPDATE: + //Get the existing data from Redis to cache, so that final validation can be done after merging this dependent data + if (c.addTableEntryForMustExp(&cfgData[i], tbl) != CVL_SUCCESS) { + c.addTableDataForMustExp(cfgData[i].VOp, tbl) + } + c.addTableEntryToCache(tbl, key) + + case OP_DELETE: + if (len(cfgData[i].Data) > 0) { + //Delete a single field + if (len(cfgData[i].Data) != 1) { + CVL_LOG(ERROR, "Only single field is allowed for field deletion") + } else { + for field, _ := range cfgData[i].Data { + if (c.checkDeleteConstraint(cfgData, tbl, key, field) != CVL_SUCCESS) { + cvlErrObj.ErrCode = CVL_SEMANTIC_ERROR + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SEMANTIC_ERROR + } + break //only one field there + } + } + } else { + //Entire entry to be deleted + if (c.checkDeleteConstraint(cfgData, tbl, key, "") != CVL_SUCCESS) { + cvlErrObj.ErrCode = CVL_SEMANTIC_ERROR + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SEMANTIC_ERROR + } + //TBD : Can we do this ? + //No entry has depedency on this key, + //remove from requestCache, we don't need any more as depedent data + //delete(c.requestCache[tbl], key) + } + + if (c.addTableEntryForMustExp(&cfgData[i], tbl) != CVL_SUCCESS) { + c.addTableDataForMustExp(cfgData[i].VOp, tbl) + } + + c.addTableEntryToCache(tbl, key) + } + } + + //Only for tracing + if (IsTraceSet()) { + jsonData := "" + + jsonDataBytes, err := json.Marshal(requestedData) + if (err == nil) { + jsonData = string(jsonDataBytes) + } else { + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SYNTAX_ERROR + } + + TRACE_LOG(TRACE_LIBYANG, "Requested JSON Data = [%s]\n", jsonData) + } + + //Step 2 : Perform syntax validation only + yang, errN := c.translateToYang(&requestedData) + if (errN.ErrCode == CVL_SUCCESS) { + if cvlErrObj, cvlRetCode := c.validateSyntax(yang); cvlRetCode != CVL_SUCCESS { + return cvlErrObj, cvlRetCode + } + } else { + return errN,errN.ErrCode + } + + //Step 3 : Check keys and update dependent data + dependentData := make(map[string]interface{}) + + for i := 0; i < cfgDataLen; i++ { + + if (cfgData[i].VType == VALIDATE_ALL || cfgData[i].VType == VALIDATE_SEMANTICS) { + + tbl, key := splitRedisKey(cfgData[i].Key) + + //Step 3.1 : Check keys + switch cfgData[i].VOp { + case OP_CREATE: + //Check key should not already exist + n, err1 := redisClient.Exists(cfgData[i].Key).Result() + if (err1 == nil && n > 0) { + //Check if key deleted and CREATE done in same session, + //allow to create the entry + deletedInSameSession := false + if tbl != "" && key != "" { + for _, cachedCfgData := range c.requestCache[tbl][key] { + if cachedCfgData.reqData.VOp == OP_DELETE { + deletedInSameSession = true + break + } + } + } + + if deletedInSameSession == false { + CVL_LOG(ERROR, "\nValidateEditConfig(): Key = %s already exists", cfgData[i].Key) + cvlErrObj.ErrCode = CVL_SEMANTIC_KEY_ALREADY_EXIST + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SEMANTIC_KEY_ALREADY_EXIST + + } else { + TRACE_LOG(TRACE_CREATE, "\nKey %s is deleted in same session, skipping key existence check for OP_CREATE operation", cfgData[i].Key) + } + } + + c.yp.SetOperation("CREATE") + + case OP_UPDATE: + n, err1 := redisClient.Exists(cfgData[i].Key).Result() + if (err1 != nil || n == 0) { //key must exists + CVL_LOG(ERROR, "\nValidateEditConfig(): Key = %s does not exist", cfgData[i].Key) + cvlErrObj.ErrCode = CVL_SEMANTIC_KEY_ALREADY_EXIST + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SEMANTIC_KEY_NOT_EXIST + } + + c.yp.SetOperation("UPDATE") + + case OP_DELETE: + n, err1 := redisClient.Exists(cfgData[i].Key).Result() + if (err1 != nil || n == 0) { //key must exists + CVL_LOG(ERROR, "\nValidateEditConfig(): Key = %s does not exist", cfgData[i].Key) + cvlErrObj.ErrCode = CVL_SEMANTIC_KEY_ALREADY_EXIST + cvlErrObj.CVLErrDetails = cvlErrorMap[cvlErrObj.ErrCode] + return cvlErrObj, CVL_SEMANTIC_KEY_NOT_EXIST + } + + c.yp.SetOperation("DELETE") + //store deleted keys + } + + //Run all custom validations + cvlErrObj= c.doCustomValidation(custvCfg, &custvCfg[i], tbl, key) + if cvlErrObj.ErrCode != CVL_SUCCESS { + return cvlErrObj,cvlErrObj.ErrCode + } + + } + } + + var depYang *yparser.YParserNode = nil + if (len(dependentData) > 0) { + depYang, errN = c.translateToYang(&dependentData) + } + //Step 4 : Perform validation + if cvlErrObj, cvlRetCode1 := c.validateSemantics(yang, depYang); + cvlRetCode1 != CVL_SUCCESS { + return cvlErrObj, cvlRetCode1 + } + + //Cache validated data + /* + if errObj := c.yp.CacheSubtree(false, yang); errObj.ErrCode != yparser.YP_SUCCESS { + TRACE_LOG(TRACE_CACHE, "Could not cache validated data") + } + */ + + c.yp.DestroyCache() + return cvlErrObj, CVL_SUCCESS +} + +/* Fetch the Error Message from CVL Return Code. */ +func GetErrorString(retCode CVLRetCode) string{ + + return cvlErrorMap[retCode] + +} + +//Validate key only +func (c *CVL) ValidateKeys(key []string) CVLRetCode { + return CVL_NOT_IMPLEMENTED +} + +//Validate key and data +func (c *CVL) ValidateKeyData(key string, data string) CVLRetCode { + return CVL_NOT_IMPLEMENTED +} + +//Validate key, field and value +func (c *CVL) ValidateFields(key string, field string, value string) CVLRetCode { + return CVL_NOT_IMPLEMENTED +} + +//Sort list of given tables as per their dependency +func (c *CVL) SortDepTables(tableList []string) ([]string, CVLRetCode) { + + //Add all the table names in graph nodes + graph := toposort.NewGraph(len(tableList)) + for ti := 0; ti < len(tableList); ti++ { + graph.AddNodes(tableList[ti]) + } + + //Add all the depedency edges for graph nodes + for ti :=0; ti < len(tableList); ti++ { + for tj :=0; tj < len(tableList); tj++ { + if (tableList[ti] == tableList[tj]) { + continue + } + + for _, leafRefs := range modelInfo.tableInfo[tableList[tj]].leafRef { + for _, leafRef := range leafRefs { + if (strings.Contains(leafRef, tableList[ti])) { + graph.AddEdge(yangToRedisTblName(tableList[tj]), + yangToRedisTblName(tableList[ti])) + } + } + } + } + } + + //Now perform topological sort + result, ret := graph.Toposort() + if ret == false { + return nil, CVL_ERROR + } + + return result, CVL_SUCCESS +} + +//Get the order list(parent then child) of tables in a given YANG module +//within a single model this is obtained using leafref relation +func (c *CVL) GetOrderedTables(yangModule string) ([]string, CVLRetCode) { + tableList := []string{} + + //Get all the table names under this model + for tblName, tblNameInfo := range modelInfo.tableInfo { + if (tblNameInfo.modelName == yangModule) { + tableList = append(tableList, tblName) + } + } + + return c.SortDepTables(tableList) +} + +func (c *CVL) addDepTables(tableMap map[string]bool, tableName string) { + + //Mark it is added in list + tableMap[tableName] = true + + //Now find all tables referred in leafref from this table + for _, leafRefs := range modelInfo.tableInfo[tableName].leafRef { + for _, leafRef := range leafRefs { + matches := reLeafRef.FindStringSubmatch(leafRef) + + //We have the leafref table name + if (matches != nil && len(matches) == 5) { //whole + 4 sub matches + c.addDepTables(tableMap, yangToRedisTblName(matches[2])) //call recursively + } + } + } +} + +//Get the list of dependent tables for a given table in a YANG module +func (c *CVL) GetDepTables(yangModule string, tableName string) ([]string, CVLRetCode) { + tableList := []string{} + tblMap := make(map[string]bool) + + c.addDepTables(tblMap, tableName) + + for tblName, _ := range tblMap { + tableList = append(tableList, tblName) + } + + //Add all the table names in graph nodes + graph := toposort.NewGraph(len(tableList)) + for ti := 0; ti < len(tableList); ti++ { + graph.AddNodes(tableList[ti]) + } + + //Add all the depedency edges for graph nodes + for ti :=0; ti < len(tableList); ti++ { + for tj :=0; tj < len(tableList); tj++ { + if (tableList[ti] == tableList[tj]) { + continue + } + + for _, leafRefs := range modelInfo.tableInfo[tableList[tj]].leafRef { + for _, leafRef := range leafRefs { + if (strings.Contains(leafRef, tableList[ti]+"/")) { + graph.AddEdge(yangToRedisTblName(tableList[tj]), + yangToRedisTblName(tableList[ti])) + } + } + } + } + } + + //Now perform topological sort + result, ret := graph.Toposort() + if ret == false { + return nil, CVL_ERROR + } + + return result, CVL_SUCCESS +} + +//Get the dependent data (redis keys) for an entry to be deleted +// and dependent data to be modified +func (c *CVL) GetDepDataForDelete(redisKey string) ([]string, []string) { + + tableName, key := splitRedisKey(redisKey) + + mCmd := map[string]*redis.StringSliceCmd{} + mFilterScripts := map[string]string{} + pipe := redisClient.Pipeline() + + for _, refTbl := range modelInfo.tableInfo[tableName].refFromTables { + + //check if ref field is a key + numKeys := len(modelInfo.tableInfo[refTbl.tableName].keys) + idx := 0 + for ; idx < numKeys; idx++ { + if (modelInfo.tableInfo[refTbl.tableName].keys[idx] == refTbl.field) { + //field is key comp + mCmd[refTbl.tableName] = pipe.Keys(fmt.Sprintf("%s|*%s*", + refTbl.tableName, key)) //write into pipeline} + break + } + } + + if (idx == numKeys) { + //field is hash-set field, not a key, match with hash-set field + //prepare the lua filter script + // ex: (h.members: == 'Ethernet4,' or (string.find(h['members@'], 'Ethernet4') != nil) + //',' to include leaf-list case + mFilterScripts[refTbl.tableName] = + fmt.Sprintf("return (h.%s == '%s') or " + + "h['ports@'] ~= nil and (string.find(h['%s@'], '%s') ~= nil)", + refTbl.field, key, refTbl.field, key) + } + } + + _, err := pipe.Exec() + if err != nil { + CVL_LOG(ERROR, "Failed to fetch dependent key details for table %s", tableName) + } + pipe.Close() + + //Add dependent keys which should be modified + depKeysForMod := []string{} + for tableName, mFilterScript := range mFilterScripts { + refKeys, err := luaScripts["filter_keys"].Run(redisClient, []string{}, + tableName + "|*", "", mFilterScript).Result() + + if (err != nil) { + CVL_LOG(ERROR, "Lua script error (%v)", err) + } + if (refKeys == nil) { + //No reference field found + continue + } + + refKeysStr := string(refKeys.(string)) + + if (refKeysStr != "") { + //Add all keys whose fields to be updated + depKeysForMod = append(depKeysForMod, + strings.Split(refKeysStr, ",")...) + } + } + + depKeys := []string{} + for tblName, keys := range mCmd { + res, err := keys.Result() + if (err != nil) { + CVL_LOG(ERROR, "Failed to fetch dependent key details for table %s", tblName) + continue + } + + //Add keys found + depKeys = append(depKeys, res...) + + //For each key, find dependent data for delete recursively + for i :=0; i< len(res); i++ { + retDepKeys, retDepKeysForMod := c.GetDepDataForDelete(res[i]) + depKeys = append(depKeys, retDepKeys...) + depKeysForMod = append(depKeysForMod, retDepKeysForMod...) + } + } + + return depKeys, depKeysForMod +} diff --git a/src/cvl/cvl_cache.go b/src/cvl/cvl_cache.go new file mode 100644 index 0000000000..c2a76b0e7c --- /dev/null +++ b/src/cvl/cvl_cache.go @@ -0,0 +1,469 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package cvl +import ( + "fmt" + "encoding/json" + "github.com/go-redis/redis" + . "cvl/internal/util" + "cvl/internal/yparser" + "time" + "runtime" + "github.com/antchfx/jsonquery" +) + +func (c *CVL) addTableEntryToCache(tableName string, redisKey string) { + if (tableName == "" || redisKey == "") { + return + } + + if (c.tmpDbCache[tableName] == nil) { + c.tmpDbCache[tableName] = map[string]interface{}{redisKey: nil} + } else { + tblMap := c.tmpDbCache[tableName] + tblMap.(map[string]interface{})[redisKey] =nil + c.tmpDbCache[tableName] = tblMap + } +} + +//Add the data which are referring this key +func (c *CVL) updateDeleteDataToCache(tableName string, redisKey string) { + if _, existing := c.tmpDbCache[tableName]; existing == false { + return + } else { + tblMap := c.tmpDbCache[tableName] + if _, existing := tblMap.(map[string]interface{})[redisKey]; existing == true { + delete(tblMap.(map[string]interface{}), redisKey) + c.tmpDbCache[tableName] = tblMap + } + } +} + +// Fetch dependent data from validated data cache, +// Returns the data and flag to indicate that if requested data +// is found in update request, the data should be merged with Redis data +func (c *CVL) fetchDataFromRequestCache(tableName string, key string) (d map[string]string, m bool) { + defer func() { + pd := &d + pm := &m + + TRACE_LOG(TRACE_CACHE, + "Returning data from request cache, data = %v, merge needed = %v", + *pd, *pm) + }() + + cfgDataArr := c.requestCache[tableName][key] + if (cfgDataArr != nil) { + for _, cfgReqData := range cfgDataArr { + //Delete request doesn't have depedent data + if (cfgReqData.reqData.VOp == OP_CREATE) { + return cfgReqData.reqData.Data, false + } else if (cfgReqData.reqData.VOp == OP_UPDATE) { + return cfgReqData.reqData.Data, true + } + } + } + + return nil, false +} + +//Fetch given table entries using pipeline +func (c *CVL) fetchTableDataToTmpCache(tableName string, dbKeys map[string]interface{}) int { + + TRACE_LOG(TRACE_CACHE, "\n%v, Entered fetchTableDataToTmpCache", time.Now()) + + totalCount := len(dbKeys) + if (totalCount == 0) { + //No entry to be fetched + return 0 + } + + entryFetched := 0 + bulkCount := 0 + bulkKeys := []string{} + for dbKey, val := range dbKeys { //for all keys + + if (val != nil) { //skip entry already fetched + mapTable := c.tmpDbCache[tableName] + delete(mapTable.(map[string]interface{}), dbKey) //delete entry already fetched + totalCount = totalCount - 1 + if(bulkCount != totalCount) { + //If some entries are remaining go back to 'for' loop + continue + } + } else { + //Accumulate entries to be fetched + bulkKeys = append(bulkKeys, dbKey) + bulkCount = bulkCount + 1 + } + + if(bulkCount != totalCount) && ((bulkCount % MAX_BULK_ENTRIES_IN_PIPELINE) != 0) { + //If some entries are remaining and bulk bucket is not filled, + //go back to 'for' loop + continue + } + + mCmd := map[string]*redis.StringStringMapCmd{} + + pipe := redisClient.Pipeline() + + for _, dbKey := range bulkKeys { + + redisKey := tableName + modelInfo.tableInfo[tableName].redisKeyDelim + dbKey + //Check in validated cache first and add as dependent data + if entry, mergeNeeded := c.fetchDataFromRequestCache(tableName, dbKey); (entry != nil) { + entryFetched = entryFetched + 1 + //Entry found in validated cache, so skip fetching from Redis + //if merging is not required with Redis DB + if (mergeNeeded == false) { + fieldMap := c.checkFieldMap(&entry) + c.tmpDbCache[tableName].(map[string]interface{})[dbKey] = fieldMap + continue + } + c.tmpDbCache[tableName].(map[string]interface{})[dbKey] = entry + } + + //Otherwise fetch it from Redis + mCmd[dbKey] = pipe.HGetAll(redisKey) //write into pipeline + if mCmd[dbKey] == nil { + CVL_LOG(ERROR, "Failed pipe.HGetAll('%s')", redisKey) + } + } + + _, err := pipe.Exec() + if err != nil { + CVL_LOG(ERROR, "Failed to fetch details for table %s", tableName) + return 0 + } + pipe.Close() + bulkKeys = nil + + mapTable := c.tmpDbCache[tableName] + + for key, val := range mCmd { + res, err := val.Result() + if (err != nil || len(res) == 0) { + //no data found, don't keep blank entry + delete(mapTable.(map[string]interface{}), key) + continue + } + //exclude table name and delim + keyOnly := key + + if (len(mapTable.(map[string]interface{})) > 0) && (mapTable.(map[string]interface{})[keyOnly] != nil) { + tmpFieldMap := (mapTable.(map[string]interface{})[keyOnly]).(map[string]string) + //merge with validated cache data + mergeMap(res, tmpFieldMap) + fieldMap := c.checkFieldMap(&res) + mapTable.(map[string]interface{})[keyOnly] = fieldMap + } else { + fieldMap := c.checkFieldMap(&res) + mapTable.(map[string]interface{})[keyOnly] = fieldMap + } + + entryFetched = entryFetched + 1 + } + + runtime.Gosched() + } + + TRACE_LOG(TRACE_CACHE,"\n%v, Exiting fetchTableDataToTmpCache", time.Now()) + + return entryFetched +} + +//populate redis data to cache +func (c *CVL) fetchDataToTmpCache() *yparser.YParserNode { + TRACE_LOG(TRACE_CACHE, "\n%v, Entered fetchToTmpCache", time.Now()) + + entryToFetch := 0 + var root *yparser.YParserNode = nil + var errObj yparser.YParserError + + for entryToFetch = 1; entryToFetch > 0; { //Force to enter the loop for first time + //Repeat until all entries are fetched + entryToFetch = 0 + for tableName, dbKeys := range c.tmpDbCache { //for each table + entryToFetch = entryToFetch + c.fetchTableDataToTmpCache(tableName, dbKeys.(map[string]interface{})) + } //for each table + + //If no table entry delete the table itself + for tableName, dbKeys := range c.tmpDbCache { //for each table + if (len(dbKeys.(map[string]interface{})) == 0) { + delete(c.tmpDbCache, tableName) + continue + } + } + + if (entryToFetch == 0) { + //No more entry to fetch + break + } + + if (Tracing == true) { + jsonDataBytes, _ := json.Marshal(c.tmpDbCache) + jsonData := string(jsonDataBytes) + TRACE_LOG(TRACE_CACHE, "Top Node=%v\n", jsonData) + } + + data, err := jsonquery.ParseJsonMap(&c.tmpDbCache) + + if (err != nil) { + return nil + } + + //Build yang tree for each table and cache it + for jsonNode := data.FirstChild; jsonNode != nil; jsonNode=jsonNode.NextSibling { + TRACE_LOG(TRACE_CACHE, "Top Node=%v\n", jsonNode.Data) + //Visit each top level list in a loop for creating table data + topNode, _ := c.generateTableData(true, jsonNode) + if (root == nil) { + root = topNode + } else { + if root, errObj = c.yp.MergeSubtree(root, topNode); errObj.ErrCode != yparser.YP_SUCCESS { + return nil + } + } + } + } // until all dependent data is fetched + + if root != nil && Tracing == true { + dumpStr := c.yp.NodeDump(root) + TRACE_LOG(TRACE_CACHE, "Dependent Data = %v\n", dumpStr) + } + + TRACE_LOG(TRACE_CACHE, "\n%v, Exiting fetchToTmpCache", time.Now()) + return root +} + + +func (c *CVL) clearTmpDbCache() { + for key, _ := range c.tmpDbCache { + delete(c.tmpDbCache, key) + } +} + +//Get table entry from cache for redis key +func dbCacheEntryGet(tableName, key string) (*yparser.YParserNode, CVLRetCode) { + //First check if the table is cached + topNode, _ := dbCacheGet(tableName) + + + if (topNode != nil) { + //Convert to Yang keys + keyValuePair := getRedisToYangKeys(tableName, key) + + //Find if the entry is cached + keyCompStr := "" + for _, keyValItem := range keyValuePair { + keyCompStr = keyCompStr + fmt.Sprintf("[%s='%s']", + keyValItem.key, keyValItem.values[0]) + } + + entryNode := yparser.FindNode(topNode, fmt.Sprintf("//%s:%s/%s%s", + modelInfo.tableInfo[tableName].modelName, + modelInfo.tableInfo[tableName].modelName, + tableName, keyCompStr)) + + if (entryNode != nil) { + return entryNode, CVL_SUCCESS + } + } + + return nil, CVL_ERROR +} + +//Get the data from global cache +func dbCacheGet(tableName string) (*yparser.YParserNode, CVLRetCode) { + + TRACE_LOG(TRACE_CACHE, "Updating global cache for table %s", tableName) + dbCacheTmp, existing := cvg.db[tableName] + + if (existing == false) { + return nil, CVL_FAILURE //not even empty cache present + } + + if (dbCacheTmp.root != nil) { + if (dbCacheTmp.expiry != 0) { + //If cache is destroyable (i.e. expiry != 0), check if it has already expired. + //If not expired update the time stamp + if (time.Now().After(dbCacheTmp.startTime.Add(time.Second * time.Duration(dbCacheTmp.expiry)))) { + //Cache expired, clear the cache + dbCacheClear(tableName) + + return nil, CVL_ERROR + } + + //Since the cache is used actively, update the timestamp + dbCacheTmp.startTime = time.Now() + cvg.db[tableName] = dbCacheTmp + } + + return dbCacheTmp.root, CVL_SUCCESS + } else { + return nil, CVL_SUCCESS // return success for no entry in Redis db and hencec empty cache + } +} + +//Get the table data from redis and cache it in yang node format +//expiry =0 never expire the cache +func dbCacheSet(update bool, tableName string, expiry uint16) CVLRetCode { + + cvg.mutex.Lock() + + //Get the data from redis and save it + tableKeys, err:= redisClient.Keys(tableName + + modelInfo.tableInfo[tableName].redisKeyDelim + "*").Result() + + if (err != nil) { + cvg.mutex.Unlock() + return CVL_FAILURE + } + + TRACE_LOG(TRACE_CACHE, "Building global cache for table %s", tableName) + + tablePrefixLen := len(tableName + modelInfo.tableInfo[tableName].redisKeyDelim) + for _, tableKey := range tableKeys { + tableKey = tableKey[tablePrefixLen:] //remove table prefix + if (cvg.cv.tmpDbCache[tableName] == nil) { + cvg.cv.tmpDbCache[tableName] = map[string]interface{}{tableKey: nil} + } else { + tblMap := cvg.cv.tmpDbCache[tableName] + tblMap.(map[string]interface{})[tableKey] =nil + cvg.cv.tmpDbCache[tableName] = tblMap + } + } + + cvg.db[tableName] = dbCachedData{startTime:time.Now(), expiry: expiry, + root: cvg.cv.fetchDataToTmpCache()} + + if (Tracing == true) { + TRACE_LOG(TRACE_CACHE, "Cached Data = %v\n", cvg.cv.yp.NodeDump(cvg.db[tableName].root)) + } + + cvg.mutex.Unlock() + + //install keyspace notification for updating the cache + if (update == false) { + installDbChgNotif() + } + + + return CVL_SUCCESS +} + +//Receive all updates for all tables on a single channel +func installDbChgNotif() { + if (len(cvg.db) > 1) { //notif running for at least one table added previously + cvg.stopChan <- 1 //stop active notification + } + + subList := make([]string, 0) + for tableName, _ := range cvg.db { + subList = append(subList, + fmt.Sprintf("__keyspace@%d__:%s%s*", modelInfo.tableInfo[tableName].dbNum, + tableName, modelInfo.tableInfo[tableName].redisKeyDelim)) + + } + + //Listen on multiple channels + cvg.pubsub = redisClient.PSubscribe(subList...) + + go func() { + keySpacePrefixLen := len("__keyspace@4__:") + + notifCh := cvg.pubsub.Channel() + for { + select { + case <-cvg.stopChan: + //stop this routine + return + case msg:= <-notifCh: + //Handle update + tbl, key := splitRedisKey(msg.Channel[keySpacePrefixLen:]) + if (tbl != "" && key != "") { + dbCacheUpdate(tbl, key, msg.Payload) + } + } + } + }() +} + +func dbCacheUpdate(tableName, key, op string) CVLRetCode { + TRACE_LOG(TRACE_CACHE, "Updating global cache for table %s with key %s", tableName, key) + + //Find the node + //Delete the entry in yang tree + + cvg.mutex.Lock() + + node, _:= dbCacheEntryGet(tableName, key) + if (node != nil) { + //unlink and free the node + cvg.cv.yp.FreeNode(node) + } + + //Clear json map cache if any + cvg.cv.clearTmpDbCache() + + tableKeys := []string {key} + switch op { + case "hset", "hmset", "hdel": + //Get the entry from DB + for _, tableKey := range tableKeys { + cvg.cv.tmpDbCache[tableName] = map[string]interface{}{tableKey: nil} + } + + //Get the translated Yang tree + topNode := cvg.cv.fetchDataToTmpCache() + + //Merge the subtree with existing yang tree + var errObj yparser.YParserError + if (cvg.db[tableName].root != nil) { + if topNode, errObj = cvg.cv.yp.MergeSubtree(cvg.db[tableName].root, topNode); errObj.ErrCode != yparser.YP_SUCCESS { + cvg.mutex.Unlock() + return CVL_ERROR + } + } + + //Update DB map + db := cvg.db[tableName] + db.root = topNode + cvg.db[tableName] = db + + case "del": + //NOP, already deleted the entry + } + + cvg.mutex.Unlock() + + return CVL_SUCCESS +} + +//Clear cache data for given table +func dbCacheClear(tableName string) CVLRetCode { + cvg.cv.yp.FreeNode(cvg.db[tableName].root) + delete(cvg.db, tableName) + + TRACE_LOG(TRACE_CACHE, "Clearing global cache for table %s", tableName) + + return CVL_SUCCESS +} + diff --git a/src/cvl/cvl_luascript.go b/src/cvl/cvl_luascript.go new file mode 100644 index 0000000000..ff1ffb07ca --- /dev/null +++ b/src/cvl/cvl_luascript.go @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" +) + +//Redis server side script +func loadLuaScript(luaScripts map[string]*redis.Script) { + + // Find entry which has given fieldName and value + luaScripts["find_key"] = redis.NewScript(` + local tableName=ARGV[1] + local sep=ARGV[2] + local fieldName=ARGV[3] + local fieldValue=ARGV[4] + + -- Check if field value is part of key + local entries=redis.call('KEYS', tableName..sep.."*"..fieldValue.."*") + + if (entries[1] ~= nil) + then + return entries[1] + else + + -- Search through all keys for fieldName and fieldValue + local entries=redis.call('KEYS', tableName..sep.."*") + + local idx = 1 + while(entries[idx] ~= nil) + do + local val = redis.call("HGET", entries[idx], fieldName) + if (val == fieldValue) + then + -- Return the key + return entries[idx] + end + + idx = idx + 1 + end + end + + -- Could not find the key + return "" + `) + + //Find current number of entries in a table + luaScripts["count_entries"] = redis.NewScript(` + return #redis.call('KEYS', ARGV[1].."*") + `) + + // Find entry which has given fieldName and value + luaScripts["filter_keys"] = redis.NewScript(` + --ARGV[1] => Key patterns + --ARGV[2] => Key names separated by '|' + --ARGV[3] => predicate patterns + local filterKeys = "" + + local keys = redis.call('KEYS', ARGV[1]) + if #keys == 0 then return end + + local sepStart = string.find(keys[1], "|") + if sepStart == nil then return ; end + + -- Function to load lua predicate code + local function loadPredicateScript(str) + if (str == nil or str == "") then return nil; end + + local f, err = loadstring("return function (k,h) " .. str .. " end") + if f then return f(); else return nil;end + end + + local keySetNames = {} + ARGV[2]:gsub("([^|]+)",function(c) table.insert(keySetNames, c) end) + + local predicate = loadPredicateScript(ARGV[3]) + + for _, key in ipairs(keys) do + local hash = redis.call('HGETALL', key) + local row = {}; local keySet = {}; local keyVal = {} + local keyOnly = string.sub(key, sepStart+1) + + for index = 1, #hash, 2 do + row[hash[index]] = hash[index + 1] + end + + --Split key values + keyOnly:gsub("([^|]+)", function(c) table.insert(keyVal, c) end) + + for idx = 1, #keySetNames, 1 do + keySet[keySetNames[idx]] = keyVal[idx] + end + + if (predicate == nil) or (predicate(keySet, row) == true) then + filterKeys = filterKeys .. key .. "," + end + + end + + return string.sub(filterKeys, 1, #filterKeys - 1) + `) +} diff --git a/src/cvl/cvl_semantics.go b/src/cvl/cvl_semantics.go new file mode 100644 index 0000000000..acee42f606 --- /dev/null +++ b/src/cvl/cvl_semantics.go @@ -0,0 +1,627 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package cvl + +import ( + "fmt" + "encoding/xml" + "strings" +// "github.com/antchfx/xpath" + "github.com/antchfx/xmlquery" + "github.com/antchfx/jsonquery" + . "cvl/internal/util" +) + +//YANG Validator used for external semantic +//validation including custom/platform validation +type YValidator struct { + root *xmlquery.Node //Top evel root for data + current *xmlquery.Node //Current position + operation string //Edit operation +} + +//Generate leaf/leaf-list YANG data +func (c *CVL) generateYangLeafData(tableName string, jsonNode *jsonquery.Node, +parent *xmlquery.Node) CVLRetCode { + + //Traverse fields + for jsonFieldNode := jsonNode.FirstChild; jsonFieldNode!= nil; + jsonFieldNode = jsonFieldNode.NextSibling { + //Add fields as leaf to the list + if (jsonFieldNode.Type == jsonquery.ElementNode && + jsonFieldNode.FirstChild != nil && + jsonFieldNode.FirstChild.Type == jsonquery.TextNode) { + + if (len(modelInfo.tableInfo[tableName].mapLeaf) == 2) {//mapping should have two leaf always + //Values should be stored inside another list as map table + listNode := c.addYangNode(tableName, parent, tableName, "") //Add the list to the top node + c.addYangNode(tableName, + listNode, modelInfo.tableInfo[tableName].mapLeaf[0], + jsonFieldNode.Data) + + c.addYangNode(tableName, + listNode, modelInfo.tableInfo[tableName].mapLeaf[1], + jsonFieldNode.FirstChild.Data) + + } else { + //check if it is hash-ref, then need to add only key from "TABLE|k1" + hashRefMatch := reHashRef.FindStringSubmatch(jsonFieldNode.FirstChild.Data) + + if (hashRefMatch != nil && len(hashRefMatch) == 3) { + c.addYangNode(tableName, + parent, jsonFieldNode.Data, + hashRefMatch[2]) //take hashref key value + } else { + c.addYangNode(tableName, + parent, jsonFieldNode.Data, + jsonFieldNode.FirstChild.Data) + } + } + + } else if (jsonFieldNode.Type == jsonquery.ElementNode && + jsonFieldNode.FirstChild != nil && + jsonFieldNode.FirstChild.Type == jsonquery.ElementNode) { + //Array data e.g. VLAN members@ or 'ports@' + for arrayNode:=jsonFieldNode.FirstChild; arrayNode != nil; + arrayNode = arrayNode.NextSibling { + + node := c.addYangNode(tableName, + parent, jsonFieldNode.Data, + arrayNode.FirstChild.Data) + + //mark these nodes as leaf-list + addAttrNode(node, "leaf-list", "") + } + } + } + + return CVL_SUCCESS +} + +//Add attribute YANG node +func addAttrNode(n *xmlquery.Node, key, val string) { + var attr xml.Attr + attr = xml.Attr{ + Name: xml.Name{Local: key}, + Value: val, + } + + n.Attr = append(n.Attr, attr) +} + +//Add YANG node with or without parent, with or without value +func (c *CVL) addYangNode(tableName string, parent *xmlquery.Node, + name string, value string) *xmlquery.Node { + + //Create the node + node := &xmlquery.Node{Parent: parent, Data: name, + Type: xmlquery.ElementNode} + + //Set prefix from parent + if (parent != nil) { + node.Prefix = parent.Prefix + } + + if (value != "") { + //Create the value node + textNode := &xmlquery.Node{Data: value, Type: xmlquery.TextNode} + node.FirstChild = textNode + node.LastChild = textNode + } + + if (parent == nil) { + //Creating top node + return node + } + + if parent.FirstChild == nil { + //Create as first child + parent.FirstChild = node + parent.LastChild = node + + } else { + //Append as sibling + tmp := parent.LastChild + tmp.NextSibling = node + node.PrevSibling = tmp + parent.LastChild = node + } + + return node +} + +//Generate YANG list data along with top container, +//table container. +//If needed, stores the list pointer against each request table/key +//in requestCahce so that YANG data can be reached +//directly on given table/key +func (c *CVL) generateYangListData(jsonNode *jsonquery.Node, + storeInReqCache bool)(*xmlquery.Node, 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 *xmlquery.Node + + if _, exists := modelInfo.tableInfo[tableName]; exists == false { + CVL_LOG(ERROR, "Failed to find schema details for table %s", tableName) + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.TableName = tableName + cvlErrObj.Msg ="Schema details not found" + return nil, cvlErrObj + } + + // Add top most conatiner e.g. 'container sonic-acl {...}' + topNode = c.addYangNode(tableName, nil, modelInfo.tableInfo[tableName].modelName, "") + topNode.Prefix = modelInfo.modelNs[modelInfo.tableInfo[tableName].modelName].prefix + topNode.NamespaceURI = modelInfo.modelNs[modelInfo.tableInfo[tableName].modelName].ns + + //Add the container node for each list + //e.g. 'container ACL_TABLE { list ACL_TABLE_LIST ...} + listConatinerNode := c.addYangNode(tableName, topNode, tableName, "") + + //Traverse each key instance + for jsonNode = jsonNode.FirstChild; jsonNode != nil; jsonNode = jsonNode.NextSibling { + //store the redis key + redisKey := jsonNode.Data + + //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, redisKey); yangListName!= "" { + tableName = yangListName + } + keyValuePair := getRedisToYangKeys(tableName, redisKey) + keyCompCount := len(keyValuePair) + totalKeyComb := 1 + var keyIndices []int + + //Find number of all key combinations + //Each key can have one or more key values, which results in nk1 * nk2 * nk2 combinations + idx := 0 + for i,_ := range keyValuePair { + totalKeyComb = totalKeyComb * len(keyValuePair[i].values) + keyIndices = append(keyIndices, 0) + } + + 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.addYangNode(tableName, listConatinerNode, tableName + "_LIST", "") //Add the list to the top node + addAttrNode(listNode, "key", redisKey) + + if (storeInReqCache == true) { + //store the list pointer in requestCache against the table/key + reqCache, exists := c.requestCache[tableName][redisKey] + if exists == true { + //Store same list instance in all requests under same table/key + for idx := 0; idx < len(reqCache); idx++ { + reqCache[idx].yangData = listNode + } + } + } + + //For each key combination + //Add keys as leaf to the list + for idx = 0; idx < keyCompCount; idx++ { + c.addYangNode(tableName, listNode, keyValuePair[idx].key, + keyValuePair[idx].values[keyIndices[idx]]) + } + + //Get all fields under the key field and add them as children of the list + c.generateYangLeafData(tableName, jsonNode, listNode) + + //Check which key elements left after current key element + var next int = keyCompCount - 1 + for ((next > 0) && ((keyIndices[next] +1) >= len(keyValuePair[next].values))) { + next-- + } + //No more combination possible + if (next < 0) { + break + } + + keyIndices[next]++ + + //Reset indices for all other key elements + for idx = next+1; idx < keyCompCount; idx++ { + keyIndices[idx] = 0 + } + } + } + + return topNode, cvlErrObj +} + +//Append given children to destNode +func (c *CVL) appendSubtree(dest, src *xmlquery.Node) CVLRetCode { + if (dest == nil || src == nil) { + return CVL_FAILURE + } + + var lastSibling *xmlquery.Node = nil + + for srcNode := src; srcNode != nil ; srcNode = srcNode.NextSibling { + //set parent for all nodes + srcNode.Parent = dest + lastSibling = srcNode + } + + if (dest.LastChild == nil) { + //No sibling in dest yet + dest.FirstChild = src + dest.LastChild = lastSibling + } else { + //Append to the last sibling + dest.LastChild.NextSibling = src + src.PrevSibling = dest.LastChild + dest.LastChild = lastSibling + } + + return CVL_SUCCESS +} + +//Return subtree after detaching from parent +func (c *CVL) detachSubtree(parent *xmlquery.Node) *xmlquery.Node { + + child := parent.FirstChild + + if (child != nil) { + //set children to nil + parent.FirstChild = nil + parent.LastChild = nil + } else { + //No children + return nil + } + + //Detach all children from parent + for node := child; node != nil; node = node.NextSibling { + node.Parent = nil + } + + return child +} + +//Detach a node from its parent +func (c *CVL) detachNode(node *xmlquery.Node) CVLRetCode { + if (node == nil) { + return CVL_FAILURE + } + + //get the parent node + parent := node.Parent + + //adjust siblings + if (parent.FirstChild == node && parent.LastChild == node) { + //this is the only node + parent.FirstChild = nil + parent.LastChild = nil + } else if (parent.FirstChild == node) { + //first child, set new first child + parent.FirstChild = node.NextSibling + node.NextSibling.PrevSibling = nil + } else { + node.PrevSibling.NextSibling = node.NextSibling + if (node.NextSibling != nil) { + //if remaining sibling + node.NextSibling.PrevSibling = node.PrevSibling + } + } + + //detach from parent and siblings + node.Parent = nil + node.PrevSibling = nil + node.NextSibling = nil + + return CVL_SUCCESS +} + +//Delete all leaf-list nodes in destination +//Leaf-list should be replaced from source +//destination +func (c *CVL) deleteDestLeafList(dest *xmlquery.Node) { + + TRACE_LOG(TRACE_CACHE, "Updating leaf-list by " + + "removing and then adding leaf-list") + + //find start and end of dest leaf list + leafListName := dest.Data + for node := dest; node != nil; { + tmpNextNode := node.NextSibling + + if (node.Data == leafListName) { + c.detachNode(node) + node = tmpNextNode + continue + } else { + //no more leaflist node + break + } + } +} + +//Merge YANG data recursively from dest to src +//Leaf-list is always replaced and appeneded at +//the end of list's children +func (c *CVL) mergeYangData(dest, src *xmlquery.Node) CVLRetCode { + TRACE_LOG((TRACE_SYNTAX | TRACE_SEMANTIC), + "Merging YANG data") + + if (dest == nil) || (src == nil) { + return CVL_FAILURE + } + + if (dest.Type ==xmlquery.TextNode) && (src.Type == xmlquery.TextNode) { + //handle leaf node by updating value + dest.Data = src.Data + return CVL_SUCCESS + } + + srcNode := src + + destLeafListDeleted := false + for srcNode != nil { + //Find all source nodes and attach to the matching destination node + ret := CVL_FAILURE +destLoop: + destNode := dest + for ; destNode != nil; destNode = destNode.NextSibling { + if (destNode.Data != srcNode.Data) { + //Can proceed to subtree only if current node name matches + continue + } + + if (strings.HasSuffix(destNode.Data, "_LIST")){ + //find exact match for list instance + //check with key value, stored in attribute + if (len(destNode.Attr) == 0) || (len(srcNode.Attr) == 0) || + (destNode.Attr[0].Value != srcNode.Attr[0].Value) { + //move to next list + continue + } + } else if (len(destNode.Attr) > 0) && (len(srcNode.Attr) > 0) && + (destNode.Attr[0].Name.Local == "leaf-list") && + (srcNode.Attr[0].Name.Local == "leaf-list") { // attribute has type + + if (destLeafListDeleted == false) { + //Replace all leaf-list nodes from destination first + c.deleteDestLeafList(destNode) + destLeafListDeleted = true + //Note that 'dest' still points to list keys + //even though all leaf-list might have been deleted + goto destLoop + } else { + //if all dest leaflist deleted, + //just break to add all leaflist + destNode = nil + break + } + } + + //Go to their children + ret = c.mergeYangData(destNode.FirstChild, srcNode.FirstChild) + + //Node matched break now + break + + } //dest node loop + + if (ret == CVL_FAILURE) { + if (destNode == nil) { + //destNode == nil -> node not found + //detach srcNode and append to dest + tmpNextSrcNode := srcNode.NextSibling + if CVL_SUCCESS == c.detachNode(srcNode) { + if (len(srcNode.Attr) > 0) && + (srcNode.Attr[0].Name.Local == "leaf-list") { + //set the flag so that we don't delete leaf-list + //from destNode further + destLeafListDeleted = true + } + c.appendSubtree(dest.Parent, srcNode) + } + srcNode = tmpNextSrcNode + continue + } else { + //subtree merge failure ,, append subtree + //appendSubtree(dest, src) + subTree := c.detachSubtree(srcNode) + if (subTree != nil) { + c.appendSubtree(destNode, subTree) + } + } + } + + srcNode = srcNode.NextSibling + } //src node loop + + return CVL_SUCCESS +} + +//Locate YANG list instance in root for given table name and key +func (c *CVL) moveToYangList(tableName string, redisKey string) *xmlquery.Node { + + var nodeTbl *xmlquery.Node = nil + //move to the model first + for node := c.yv.root.FirstChild; node != nil; node = node.NextSibling { + if (node.Data != modelInfo.tableInfo[tableName].modelName) { + continue + } + + //Move to container + for nodeTbl = node.FirstChild; nodeTbl != nil; nodeTbl = nodeTbl.NextSibling { + if (nodeTbl.Data == tableName) { + break + } + } + } + + if (nodeTbl == nil) { + CVL_LOG(WARNING, "Unable to find YANG data for table %s, key %s", + tableName, redisKey) + return nil + } + + //Move to list + for nodeList := nodeTbl.FirstChild; nodeList != nil; nodeList = nodeList.NextSibling { + if (nodeList.Data != fmt.Sprintf("%s_LIST", tableName)) { + continue + } + + c.yv.current = nodeList + //if no key specified or no other instance exists, + //just return the first list instance + if (redisKey == "" || nodeList.NextSibling == nil) { + return c.yv.current + } + + //Get key name-value pair + keyValuePair := getRedisToYangKeys(tableName, redisKey) + if (keyValuePair == nil) { + return c.yv.current + } + + //Match the key value with list instance + for ; (nodeList != nil); nodeList = nodeList.NextSibling { + keyIdx := 0; + + for nodeLeaf := nodeList.FirstChild; + (nodeLeaf != nil) && (keyIdx < len(keyValuePair)); + nodeLeaf = nodeLeaf.NextSibling { + + if (nodeLeaf.Data == keyValuePair[keyIdx].key) && + (nodeLeaf.FirstChild != nil) && + (nodeLeaf.FirstChild.Data == keyValuePair[keyIdx].values[0]) { + keyIdx = keyIdx + 1 + } + } + + //Check if all key values matched + if (keyIdx == len(keyValuePair)) { + return nodeList + } + } + } + + CVL_LOG(WARNING, "Unable to find YANG data for table %s, key %s", + tableName, redisKey) + return nil +} + +//Find which all tables (and which field) is using given (tableName/field) +// as leafref +//Use LUA script to find if table has any entry for this leafref +func (c *CVL) findUsedAsLeafRef(tableName, field string) []tblFieldPair { + + var tblFieldPairArr []tblFieldPair + + for tblName, tblInfo := range modelInfo.tableInfo { + if (tableName == tblName) { + continue + } + if (len(tblInfo.leafRef) == 0) { + continue + } + + for fieldName, leafRefs := range tblInfo.leafRef { + found := false + //Find leafref by searching table and field name + for _, leafRef := range leafRefs { + if ((strings.Contains(leafRef, tableName) == true) && + (strings.Contains(leafRef, field) == true)) { + tblFieldPairArr = append(tblFieldPairArr, + tblFieldPair{tblName, fieldName}) + //Found as leafref, no need to search further + found = true + break + } + } + + if (found == true) { + break + } + } + } + + return tblFieldPairArr +} + +//Check delete constraint for leafref if key/field is deleted +func (c *CVL) checkDeleteConstraint(cfgData []CVLEditConfigData, + tableName, keyVal, field string) CVLRetCode { + var leafRefs []tblFieldPair + if (field != "") { + //Leaf or field is getting deleted + leafRefs = c.findUsedAsLeafRef(tableName, field) + TRACE_LOG(TRACE_SEMANTIC, + "(Table %s, field %s) getting used by leafRefs %v", + tableName, field, leafRefs) + } else { + //Entire entry is getting deleted + leafRefs = c.findUsedAsLeafRef(tableName, modelInfo.tableInfo[tableName].keys[0]) + TRACE_LOG(TRACE_SEMANTIC, + "(Table %s, key %s) getting used by leafRefs %v", + tableName, keyVal, leafRefs) + } + + //The entry getting deleted might have been referred from multiple tables + //Return failure if at-least one table is using this entry + for _, leafRef := range leafRefs { + TRACE_LOG((TRACE_DELETE | TRACE_SEMANTIC), "Checking delete constraint for leafRef %s/%s", leafRef.tableName, leafRef.field) + //Check in dependent data first, if the referred entry is already deleted + leafRefDeleted := false + for _, cfgDataItem := range cfgData { + if (cfgDataItem.VType == VALIDATE_NONE) && + (cfgDataItem.VOp == OP_DELETE ) && + (strings.HasPrefix(cfgDataItem.Key, (leafRef.tableName + modelInfo.tableInfo[leafRef.tableName].redisKeyDelim + keyVal))) { + //Currently, checking for one entry is being deleted in same session + //We should check for all entries + leafRefDeleted = true + break + } + } + + if (leafRefDeleted == true) { + continue //check next leafref + } + + //Else, check if any referred enrty is present in DB + var nokey []string + refKeyVal, err := luaScripts["find_key"].Run(redisClient, nokey, leafRef.tableName, + modelInfo.tableInfo[leafRef.tableName].redisKeyDelim, leafRef.field, keyVal).Result() + if (err == nil && refKeyVal != "") { + CVL_LOG(ERROR, "Delete will violate the constraint as entry %s is referred in %s", tableName, refKeyVal) + + return CVL_SEMANTIC_ERROR + } + } + + + return CVL_SUCCESS +} + diff --git a/src/cvl/cvl_syntax.go b/src/cvl/cvl_syntax.go new file mode 100644 index 0000000000..273ad46dec --- /dev/null +++ b/src/cvl/cvl_syntax.go @@ -0,0 +1,260 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or // +// its subsidiaries. // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// // +//////////////////////////////////////////////////////////////////////////////// + +package cvl +import ( + "fmt" + "github.com/antchfx/jsonquery" + "cvl/internal/yparser" + . "cvl/internal/util" +) + +//This function should be called before adding any new entry +//Checks max-elements defined with (current number of entries +//getting added + entries already added and present in request +//cache + entries present in Redis DB) +func (c *CVL) checkMaxElemConstraint(tableName string) CVLRetCode { + var nokey []string + + if modelInfo.tableInfo[tableName].redisTableSize == -1 { + //No limit for table size + return CVL_SUCCESS + } + + redisEntries, err := luaScripts["count_entries"].Run(redisClient, nokey, tableName).Result() + curSize := int(redisEntries.(int64)) + + if err != nil { + CVL_LOG(WARNING,"Unable to fetch current size of table %s from Redis, err= %v", + tableName, err) + return CVL_FAILURE + } + + if curSize >= modelInfo.tableInfo[tableName].redisTableSize { + CVL_LOG(ERROR, "%s table size has already reached to max-elements %d", + tableName, modelInfo.tableInfo[tableName].redisTableSize) + + return CVL_SYNTAX_ERROR + } + + //Count only the entries getting created + for _, cfgDataArr := range c.requestCache[tableName] { + for _, cfgReqData := range cfgDataArr { + if (cfgReqData.reqData.VOp != OP_CREATE) { + continue + } + + curSize = curSize + 1 + if (curSize > modelInfo.tableInfo[tableName].redisTableSize) { + //Does not meet the constraint + CVL_LOG(ERROR, "Max-elements check failed for table '%s'," + + " current size = %v, size in schema = %v", + tableName, curSize, modelInfo.tableInfo[tableName].redisTableSize) + + return CVL_SYNTAX_ERROR + } + } + } + + + return CVL_SUCCESS +} + + +//Add child node to a parent node +func(c *CVL) addChildNode(tableName string, parent *yparser.YParserNode, name string) *yparser.YParserNode { + + //return C.lyd_new(parent, modelInfo.tableInfo[tableName].module, C.CString(name)) + return c.yp.AddChildNode(modelInfo.tableInfo[tableName].module, parent, name) +} + +func (c *CVL) addChildLeaf(config bool, tableName string, parent *yparser.YParserNode, name string, value string) { + + /* If there is no value then assign default space string. */ + if len(value) == 0 { + value = " " + } + + //Batch leaf creation + c.batchLeaf = c.batchLeaf + name + "#" + value + "#" + //Check if this leaf has leafref, + //If so add the add redis key to its table so that those + // details can be fetched for dependency validation + + c.addLeafRef(config, tableName, name, value) +} + +func (c *CVL) generateTableFieldsData(config bool, tableName string, jsonNode *jsonquery.Node, +parent *yparser.YParserNode) CVLRetCode { + + //Traverse fields + for jsonFieldNode := jsonNode.FirstChild; jsonFieldNode!= nil; + jsonFieldNode = jsonFieldNode.NextSibling { + //Add fields as leaf to the list + if (jsonFieldNode.Type == jsonquery.ElementNode && + jsonFieldNode.FirstChild != nil && + jsonFieldNode.FirstChild.Type == jsonquery.TextNode) { + + if (len(modelInfo.tableInfo[tableName].mapLeaf) == 2) {//mapping should have two leaf always + //Values should be stored inside another list as map table + listNode := c.addChildNode(tableName, parent, tableName) //Add the list to the top node + c.addChildLeaf(config, tableName, + listNode, modelInfo.tableInfo[tableName].mapLeaf[0], + jsonFieldNode.Data) + + c.addChildLeaf(config, tableName, + listNode, modelInfo.tableInfo[tableName].mapLeaf[1], + jsonFieldNode.FirstChild.Data) + + } else { + //check if it is hash-ref, then need to add only key from "TABLE|k1" + hashRefMatch := reHashRef.FindStringSubmatch(jsonFieldNode.FirstChild.Data) + + if (hashRefMatch != nil && len(hashRefMatch) == 3) { + /*if (strings.HasPrefix(jsonFieldNode.FirstChild.Data, "[")) && + (strings.HasSuffix(jsonFieldNode.FirstChild.Data, "]")) && + (strings.Index(jsonFieldNode.FirstChild.Data, "|") > 0) {*/ + + c.addChildLeaf(config, tableName, + parent, jsonFieldNode.Data, + hashRefMatch[2]) //take hashref key value + } else { + c.addChildLeaf(config, tableName, + parent, jsonFieldNode.Data, + jsonFieldNode.FirstChild.Data) + } + } + + } else if (jsonFieldNode.Type == jsonquery.ElementNode && + jsonFieldNode.FirstChild != nil && + jsonFieldNode.FirstChild.Type == jsonquery.ElementNode) { + //Array data e.g. VLAN members + for arrayNode:=jsonFieldNode.FirstChild; arrayNode != nil; + + arrayNode = arrayNode.NextSibling { + c.addChildLeaf(config, tableName, + parent, jsonFieldNode.Data, + arrayNode.FirstChild.Data) + } + } + } + + return CVL_SUCCESS +} + +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 { + CVL_LOG(ERROR, "Schema details not found for %s", tableName) + cvlErrObj.ErrCode = CVL_SYNTAX_ERROR + cvlErrObj.TableName = tableName + cvlErrObj.Msg ="Schema details not found" + 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 { + + //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 + var keyIndices []int + + //Find number of all key combinations + //Each key can have one or more key values, which results in nk1 * nk2 * nk2 combinations + idx := 0 + for i,_ := range keyValuePair { + totalKeyComb = totalKeyComb * len(keyValuePair[i].values) + keyIndices = append(keyIndices, 0) + } + + 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, listConatinerNode, tableName + "_LIST") //Add the list to the top node + + //For each key combination + //Add keys as leaf to the list + for idx = 0; idx < keyCompCount; idx++ { + c.addChildLeaf(config, tableName, + listNode, keyValuePair[idx].key, + keyValuePair[idx].values[keyIndices[idx]]) + } + + //Get all fields under the key field and add them as children of the list + c.generateTableFieldsData(config, tableName, jsonNode, listNode) + + //Check which key elements left after current key element + var next int = keyCompCount - 1 + for ((next > 0) && ((keyIndices[next] +1) >= len(keyValuePair[next].values))) { + next-- + } + //No more combination possible + if (next < 0) { + break + } + + keyIndices[next]++ + + //Reset indices for all other key elements + for idx = next+1; idx < keyCompCount; idx++ { + keyIndices[idx] = 0 + } + + TRACE_LOG(TRACE_CACHE, "Starting batch leaf creation - %s\n", c.batchLeaf) + //process batch leaf creation + if errObj := c.yp.AddMultiLeafNodes(modelInfo.tableInfo[tableName].module, listNode, c.batchLeaf); errObj.ErrCode != yparser.YP_SUCCESS { + cvlErrObj = createCVLErrObj(errObj) + CVL_LOG(ERROR, "Failed to create leaf nodes, data = %s", + c.batchLeaf) + return nil, cvlErrObj + } + c.batchLeaf = "" + } + } + + return topNode, cvlErrObj +} + diff --git a/src/cvl/cvl_test.go b/src/cvl/cvl_test.go new file mode 100644 index 0000000000..ed38c724e8 --- /dev/null +++ b/src/cvl/cvl_test.go @@ -0,0 +1,3858 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "cvl" + "encoding/json" + "fmt" + "github.com/go-redis/redis" + "io/ioutil" + "os" + "os/exec" + "strings" + //"syscall" + "testing" + "runtime" + . "cvl/internal/util" + //"cvl/internal/yparser" +) + +type testEditCfgData struct { + filedescription string + cfgData string + depData string + retCode cvl.CVLRetCode +} + +var rclient *redis.Client +var port_map map[string]interface{} +var filehandle *os.File + +var loadDeviceDataMap bool +var deviceDataMap = map[string]interface{} { + "DEVICE_METADATA" : map[string]interface{} { + "localhost": map[string] interface{} { + "hwsku": "Quanta-IX8-54x", + "hostname": "sonic", + "platform": "x86_64-quanta_ix8_54x-r0", + "mac": "4c:76:25:f4:70:82", + "deployment_id": "1", + }, + }, +} + +/* Dependent port channel configuration. */ +var depDataMap = map[string]interface{} { + "PORTCHANNEL" : map[string]interface{} { + "PortChannel001": map[string] interface{} { + "admin_status": "up", + "mtu": "9100", + }, + "PortChannel002": map[string] interface{} { + "admin_status": "up", + "mtu": "9100", + }, + }, + "PORTCHANNEL_MEMBER": map[string]interface{} { + "PortChannel001|Ethernet4": map[string] interface{} { + "NULL": "NULL", + }, + "PortChannel001|Ethernet8": map[string] interface{} { + "NULL": "NULL", + }, + "PortChannel001|Ethernet12": map[string] interface{} { + "NULL": "NULL", + }, + "PortChannel002|Ethernet20": map[string] interface{} { + "NULL": "NULL", + }, + "PortChannel002|Ethernet24": map[string] interface{} { + "NULL": "NULL", + }, + }, +} + +/* Converts JSON Data in a File to Map. */ +func convertJsonFileToMap(t *testing.T, fileName string) map[string]string { + var mapstr map[string]string + + jsonData := convertJsonFileToString(t, fileName) + byteData := []byte(jsonData) + + err := json.Unmarshal(byteData, &mapstr) + + if err != nil { + fmt.Println("Failed to convert Json File to map:", err) + } + + return mapstr + +} + +/* Converts JSON Data in a File to Map. */ +func convertDataStringToMap(t *testing.T, dataString string) map[string]string { + var mapstr map[string]string + + byteData := []byte(dataString) + + err := json.Unmarshal(byteData, &mapstr) + + if err != nil { + fmt.Println("Failed to convert Json Data String to map:", err) + } + + return mapstr + +} + +/* Converts JSON Data in a File to String. */ +func convertJsonFileToString(t *testing.T, fileName string) string { + var jsonData string + + data, err := ioutil.ReadFile(fileName) + + if err != nil { + fmt.Printf("\nFailed to read data file : %v\n", err) + } else { + jsonData = string(data) + } + + return jsonData +} + +/* Converts JSON config to map which can be loaded to Redis */ +func loadConfig(key string, in []byte) map[string]interface{} { + var fvp map[string]interface{} + + err := json.Unmarshal(in, &fvp) + if err != nil { + fmt.Printf("Failed to Unmarshal %v err: %v", in, err) + } + if key != "" { + kv := map[string]interface{}{} + kv[key] = fvp + return kv + } + return fvp +} + +/* Separator for keys. */ +func getSeparator() string { + return "|" +} + +/* Unloads the Config DB based on JSON File. */ +func unloadConfigDB(rclient *redis.Client, mpi map[string]interface{}) { + for key, fv := range mpi { + switch fv.(type) { + case map[string]interface{}: + for subKey, subValue := range fv.(map[string]interface{}) { + newKey := key + getSeparator() + subKey + _, err := rclient.Del(newKey).Result() + + if err != nil { + fmt.Printf("Invalid data for db: %v : %v %v", newKey, subValue, err) + } + + } + default: + fmt.Printf("Invalid data for db: %v : %v", key, fv) + } + } + +} + +/* Loads the Config DB based on JSON File. */ +func loadConfigDB(rclient *redis.Client, mpi map[string]interface{}) { + for key, fv := range mpi { + switch fv.(type) { + case map[string]interface{}: + for subKey, subValue := range fv.(map[string]interface{}) { + newKey := key + getSeparator() + subKey + _, err := rclient.HMSet(newKey, subValue.(map[string]interface{})).Result() + + if err != nil { + fmt.Printf("Invalid data for db: %v : %v %v", newKey, subValue, err) + } + + } + default: + fmt.Printf("Invalid data for db: %v : %v", key, fv) + } + } +} + +func compareErrorDetails(cvlErr cvl.CVLErrorInfo, expCode cvl.CVLRetCode, errAppTag string, constraintmsg string) bool { + + if ((cvlErr.ErrCode == expCode) && ((cvlErr.ErrAppTag == errAppTag) || (cvlErr.ConstraintErrMsg == constraintmsg))) { + return true + } + + return false +} + +func getConfigDbClient() *redis.Client { + rclient := redis.NewClient(&redis.Options{ + Network: "tcp", + Addr: "localhost:6379", + Password: "", // no password set + DB: 4, + DialTimeout: 0, + }) + _, err := rclient.Ping().Result() + if err != nil { + fmt.Printf("failed to connect to redis server %v", err) + } + return rclient +} + +/* Prepares the database in Redis Server. */ +func prepareDb() { + rclient = getConfigDbClient() + + fileName := "testdata/port_table.json" + PortsMapByte, err := ioutil.ReadFile(fileName) + if err != nil { + fmt.Printf("read file %v err: %v", fileName, err) + } + + //Load device data map on which application of deviation files depends + dm, err:= rclient.Keys("DEVICE_METADATA|localhost").Result() + if (err != nil) || (len(dm) == 0) { + loadConfigDB(rclient, deviceDataMap) + loadDeviceDataMap = true + } + + port_map = loadConfig("", PortsMapByte) + + portKeys, err:= rclient.Keys("PORT|*").Result() + //Load only the port config which are not there in Redis + if err == nil { + portMapKeys := port_map["PORT"].(map[string]interface{}) + for _, portKey := range portKeys { + //Delete the port key which is already there in Redis + delete(portMapKeys, portKey[len("PORTS|") - 1:]) + } + port_map["PORT"] = portMapKeys + } + + loadConfigDB(rclient, port_map) + loadConfigDB(rclient, depDataMap) +} + +func WriteToFile(message string) { + pc := make([]uintptr, 10) + runtime.Callers(2, pc) + f := runtime.FuncForPC(pc[0]) + + message = f.Name()+ "\n" + message + + if _, err := filehandle.Write([]byte(message)); err != nil { + fmt.Println("Unable to write to cvl test log file") + } + + message = "\n-------------------------------------------------\n" + + + if _, err := filehandle.Write([]byte(message)); err != nil { + fmt.Println("Unable to write to cvl test log file") + } +} + +/* Setup before starting of test. */ +func TestMain(m *testing.M) { + + redisAlreadyRunning := false + pidOfRedis, err := exec.Command("/bin/pidof", "redis-server").Output() + if err == nil && string(pidOfRedis) != "\n" { + redisAlreadyRunning = true + } + + if (redisAlreadyRunning == false) { + //Redis not running, lets start it + _, err := exec.Command("/bin/sh", "-c", "sudo /etc/init.d/redis-server start").Output() + if err != nil { + fmt.Println(err.Error()) + } + + } + + os.Remove("testdata/cvl_test_details.log") + + filehandle, err = os.OpenFile("testdata/cvl_test_details.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + + if err != nil { + fmt.Println("Could not open the log file for writing.") + } + + + /* Prepare the Redis database. */ + prepareDb() + SetTrace(true) + cvl.Debug(true) + code := m.Run() + //os.Exit(m.Run()) + + unloadConfigDB(rclient, port_map) + unloadConfigDB(rclient, depDataMap) + if (loadDeviceDataMap == true) { + unloadConfigDB(rclient, deviceDataMap) + } + + cvl.Finish() + rclient.Close() + rclient.FlushDB() + + if err := filehandle.Close(); err != nil { + //log.Fatal(err) + } + + if (redisAlreadyRunning == false) { + //If Redis was not already running, close the instance that we ran + _, err := exec.Command("/bin/sh", "-c", "sudo /etc/init.d/redis-server stop").Output() + if err != nil { + fmt.Println(err.Error()) + } + + } + + os.Exit(code) + +} + +//Test Initialize() API +func TestInitialize(t *testing.T) { + ret := cvl.Initialize() + if (ret != cvl.CVL_SUCCESS) { + t.Errorf("CVl initialization failed") + } + + ret = cvl.Initialize() + if (ret != cvl.CVL_SUCCESS) { + t.Errorf("CVl re-initialization should not fail") + } +} + +//Test Initialize() API +func TestFinish(t *testing.T) { + ret := cvl.Initialize() + if (ret != cvl.CVL_SUCCESS) { + t.Errorf("CVl initialization failed") + } + + cvl.Finish() + + //Initialize again for other test cases to run + cvl.Initialize() +} + +/* ValidateEditConfig with user input in file . */ +func TestValidateEditConfig_CfgFile(t *testing.T) { + + tests := []struct { + filedescription string + cfgDataFile string + depDataFile string + retCode cvl.CVLRetCode + }{ + {filedescription: "ACL_DATA", cfgDataFile: "testdata/aclrule.json", depDataFile: "testdata/acltable.json", retCode: cvl.CVL_SUCCESS}, + } + + cvSess, _ := cvl.ValidationSessOpen() + + for index, tc := range tests { + t.Logf("Running Testcase %d with Description %s", index+1, tc.filedescription) + + t.Run(tc.filedescription, func(t *testing.T) { + + jsonEditCfg_Create_DependentMap := convertJsonFileToMap(t, tc.depDataFile) + jsonEditCfg_Create_ConfigMap := convertJsonFileToMap(t, tc.cfgDataFile) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{cvl.VALIDATE_ALL, cvl.OP_CREATE, "ACL_TABLE|TestACL1", jsonEditCfg_Create_DependentMap}, + } + + + cvlErrObj, err := cvSess.ValidateEditConfig(cfgData) + + if err != tc.retCode { + t.Errorf("Config Validation failed. %v", cvlErrObj) + } + + cfgData = []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{cvl.VALIDATE_ALL, cvl.OP_CREATE, "ACL_RULE|TestACL1|Rule1", jsonEditCfg_Create_ConfigMap}, + } + + + cvlErrObj, err = cvSess.ValidateEditConfig(cfgData) + + if err != tc.retCode { + t.Errorf("Config Validation failed. %v", cvlErrObj) + } + }) + } + + cvl.ValidationSessClose(cvSess) +} + +/* ValidateEditConfig with user input inline. */ +func TestValidateEditConfig_CfgStrBuffer(t *testing.T) { + + type testStruct struct { + filedescription string + cfgData string + depData string + retCode cvl.CVLRetCode + } + + cvSess, _ := cvl.ValidationSessOpen() + + tests := []testStruct{} + + /* Iterate through data present is separate file. */ + for index, _ := range json_edit_config_create_acl_table_dependent_data { + tests = append(tests, testStruct{filedescription: "ACL_DATA", cfgData: json_edit_config_create_acl_rule_config_data[index], + depData: json_edit_config_create_acl_table_dependent_data[index], retCode: cvl.CVL_SUCCESS}) + } + + for index, tc := range tests { + t.Logf("Running Testcase %d with Description %s", index+1, tc.filedescription) + t.Run(tc.filedescription, func(t *testing.T) { + jsonEditCfg_Create_DependentMap := convertDataStringToMap(t, tc.depData) + jsonEditCfg_Create_ConfigMap := convertDataStringToMap(t, tc.cfgData) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{cvl.VALIDATE_ALL, cvl.OP_CREATE, "ACL_TABLE|TestACL1", jsonEditCfg_Create_DependentMap}, + } + + + cvlErrObj, err := cvSess.ValidateEditConfig(cfgData) + + if err != tc.retCode { + t.Errorf("Config Validation failed. %v", cvlErrObj) + } + + cfgData = []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{cvl.VALIDATE_ALL, cvl.OP_CREATE, "ACL_RULE|TestACL1|Rule1", jsonEditCfg_Create_ConfigMap}, + } + + + cvlErrObj, err = cvSess.ValidateEditConfig(cfgData) + + if err != tc.retCode { + t.Errorf("Config Validation failed. %v", cvlErrObj) + } + }) + } + + cvl.ValidationSessClose(cvSess) +} +/* API when config is given as string buffer. */ +func TestValidateConfig_CfgStrBuffer(t *testing.T) { + type testStruct struct { + filedescription string + jsonString string + retCode cvl.CVLRetCode + } + + tests := []testStruct{} + + for index, _ := range json_validate_config_data { + // Fetch the modelName. + result := strings.Split(json_validate_config_data[index], "{") + modelName := strings.Trim(strings.Replace(strings.TrimSpace(result[1]), "\"", "", -1), ":") + + tests = append(tests, testStruct{filedescription: modelName, jsonString: json_validate_config_data[index], retCode: cvl.CVL_SUCCESS}) + } + + cvSess, _ := cvl.ValidationSessOpen() + + for index, tc := range tests { + t.Logf("Running Testcase %d with Description %s", index+1, tc.filedescription) + t.Run(fmt.Sprintf("%s [%d]", tc.filedescription, index+1), func(t *testing.T) { + err := cvSess.ValidateConfig(tc.jsonString) + + + if err != tc.retCode { + t.Errorf("Config Validation failed.") + } + + }) + } + + cvl.ValidationSessClose(cvSess) + +} + + +/* API when config is given as json file. */ +func TestValidateConfig_CfgFile(t *testing.T) { + + /* Structure containing file information. */ + tests := []struct { + filedescription string + fileName string + retCode cvl.CVLRetCode + }{ + {filedescription: "Config File - VLAN,ACL,PORTCHANNEL", fileName: "testdata/config_db1.json", retCode: cvl.CVL_SUCCESS}, + } + + cvSess, _ := cvl.ValidationSessOpen() + + for index, tc := range tests { + + t.Logf("Running Testcase %d with Description %s", index+1, tc.filedescription) + t.Run(tc.filedescription, func(t *testing.T) { + jsonString := convertJsonFileToString(t, tc.fileName) + err := cvSess.ValidateConfig(jsonString) + + + if err != tc.retCode { + t.Errorf("Config Validation failed.") + } + + }) + } + + cvl.ValidationSessClose(cvSess) +} + +func TestValidateEditConfig_Delete_Must_Check_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet5" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@": "Ethernet3,Ethernet5", + }, + "TestACL2": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + "TestACL2|Rule2": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL2|Rule2", + map[string]string { + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrObj, err := cvSess.ValidateEditConfig(cfgDataAclRule) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { //should not succeed + t.Errorf("Config Validation failed. %v", cvlErrObj) + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateEditConfig_Delete_Must_Check_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet5" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@": "Ethernet3,Ethernet5", + }, + }, + "ACL_RULE" : map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL1|Rule1", + map[string]string { + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrObj, err := cvSess.ValidateEditConfig(cfgDataAclRule) + + cvl.ValidationSessClose(cvSess) + + if err == cvl.CVL_SUCCESS { //should not succeed + t.Errorf("Config Validation failed. %v", cvlErrObj) + } + + unloadConfigDB(rclient, depDataMap) +} + +//Validate invalid json data +func TestValidateConfig_Negative(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + jsonData := `{ + "VLANjunk": { + "Vlan100": { + "members": [ + "Ethernet4", + "Ethernet8" + ], + "vlanid": "100" + } + } + }` + + err := cvSess.ValidateConfig(jsonData) + + if err == cvl.CVL_SUCCESS { //Should return failure + t.Errorf("Config Validation failed.") + } + + cvl.ValidationSessClose(cvSess) +} + +/* Delete Existing Key.*/ +/* +func TestValidateEditConfig_Delete_Semantic_ACLTableReference_Positive(t *testing.T) { + + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL1005": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + "ACL_RULE": map[string]interface{} { + "TestACL1005|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL1005|Rule1", + map[string]string{}, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} +*/ + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_Valid_FieldValue(t *testing.T) { + + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + "IP_TYPE": "IPV4", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if retCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) + +} + +/* API to test edit config with invalid field value. */ +func TestValidateEditConfig_Create_Syntax_CableLength(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "CABLE_LENGTH|AZURE", + map[string]string{ + "Ethernet8": "5m", + "Ethernet12": "5m", + "Ethernet16": "5m", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +/* API to test edit config with invalid field value. */ +func TestValidateEditConfig_Create_Syntax_Invalid_FieldValue(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{cvl.VALIDATE_ALL, cvl.OP_CREATE, "ACL_TABLE|TestACL1", map[string]string{ + "stage": "INGRESS", + "type": "junk", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_Invalid_PacketAction_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD777", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + unloadConfigDB(rclient, depDataMap) + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_Invalid_SrcPrefix_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/3288888", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvl.ValidationSessClose(cvSess) + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + unloadConfigDB(rclient, depDataMap) + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_InvalidIPAddress_Negative(t *testing.T) { + + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1a.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + unloadConfigDB(rclient, depDataMap) + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_OutofBound_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "19099090909090", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + unloadConfigDB(rclient, depDataMap) + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_InvalidProtocol_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "10388888", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + +/* API to test edit config with valid syntax. */ +//Note: Syntax check is done first before dependency check +//hence ACL_TABLE is not required here +func TestValidateEditConfig_Create_Syntax_InvalidRange_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "777779000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + unloadConfigDB(rclient, depDataMap) + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_InvalidCharNEw_Negative(t *testing.T) { + + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1jjjj|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Syntax_SpecialChar_Positive(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule@##", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSessNew, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSessNew.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSessNew) + + if err != cvl.CVL_SUCCESS { //Should succeed + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) + +} + +func TestValidateEditConfig_Create_Syntax_InvalidKeyName_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "AC&&***L_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Semantic_AdditionalInvalidNode_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL1": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + "extra": "shhs", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateEditConfig_Create_Semantic_MissingMandatoryNode_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan101", + map[string]string{ + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Create_Syntax_Invalid_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULERule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Syntax_IncompleteKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Syntax_InvalidKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +/* +func TestValidateEditConfig_Update_Syntax_DependentData_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "MIRROR_SESSION|everflow", + map[string]string{ + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|MyACL11_ACL_IPV4|RULE_1", + map[string]string{ + "MIRROR_ACTION": "everflow", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrObj, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrObj)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrObj) + } + +} + +func TestValidateEditConfig_Create_Syntax_DependentData_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL|ch1", + map[string]string{ + "admin_status": "up", + "mtu": "9100", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL|ch2", + map[string]string{ + "admin_status": "up", + "mtu": "9100", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch1|Ethernet4", + map[string]string{}, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch1|Ethernet8", + map[string]string{}, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch2|Ethernet12", + map[string]string{}, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch2|Ethernet16", + map[string]string{}, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch2|Ethernet20", + map[string]string{}, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "102", + "members@": "Ethernet24,ch1,Ethernet8", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} +*/ + +func TestValidateEditConfig_Delete_Syntax_InvalidKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Update_Syntax_InvalidKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Delete_InvalidKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL1:Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrObj, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrObj)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrObj) + } + +} + +func TestValidateEditConfig_Update_Semantic_Invalid_Key_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|TestACL1Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103uuuu", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Delete_Semantic_Positive(t *testing.T) { + depDataMap := map[string]interface{}{ + "MIRROR_SESSION": map[string]interface{}{ + "everflow": map[string]interface{}{ + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "MIRROR_SESSION|everflow", + map[string]string{}, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) + +} + +func TestValidateEditConfig_Delete_Semantic_KeyNotExisting_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "MIRROR_SESSION|everflow0", + map[string]string{}, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Update_Semantic_MissingKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|TestACL177|Rule1", + map[string]string{ + "MIRROR_ACTION": "everflow", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Duplicate_Key_Negative(t *testing.T) { + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{} { + "TestACL100": map[string]interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + //Load same key in DB + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL100", + map[string]string{ + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if retCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Update_Semantic_Positive(t *testing.T) { + + // Create ACL Table. + fileName := "testdata/create_acl_table.json" + aclTableMapByte, err := ioutil.ReadFile(fileName) + if err != nil { + fmt.Printf("read file %v err: %v", fileName, err) + } + + mpi_acl_table_map := loadConfig("", aclTableMapByte) + loadConfigDB(rclient, mpi_acl_table_map) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_TABLE|TestACL1", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if retCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, mpi_acl_table_map) + +} + +/* API to test edit config with valid syntax. */ +func TestValidateConfig_Update_Semantic_Vlan_Negative(t *testing.T) { + + cvSess, _ := cvl.ValidationSessOpen() + + jsonData := `{ + "VLAN": { + "Vlan100": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "107" + } + } + }` + + err := cvSess.ValidateConfig(jsonData) + + if err == cvl.CVL_SUCCESS { //Expected semantic failure + t.Errorf("Config Validation failed -- error details.") + } + + cvl.ValidationSessClose(cvSess) +} + +func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Positive(t *testing.T) { + + // Create ACL Table. + fileName := "testdata/create_acl_table13.json" + aclTableMapByte, err := ioutil.ReadFile(fileName) + if err != nil { + fmt.Printf("read file %v err: %v", fileName, err) + } + + mpi_acl_table_map := loadConfig("", aclTableMapByte) + loadConfigDB(rclient, mpi_acl_table_map) + + // Create ACL Rule. + fileName = "testdata/acl_rule.json" + aclTableMapRule, err := ioutil.ReadFile(fileName) + if err != nil { + fmt.Printf("read file %v err: %v", fileName, err) + } + + mpi_acl_table_rule := loadConfig("", aclTableMapRule) + loadConfigDB(rclient, mpi_acl_table_rule) + + depDataMap := map[string]interface{}{ + "MIRROR_SESSION": map[string]interface{}{ + "everflow2": map[string]interface{}{ + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + /* ACL and Rule name pre-created . */ + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|TestACL13|Rule1", + map[string]string{ + "MIRROR_ACTION": "everflow2", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if retCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, mpi_acl_table_map) + unloadConfigDB(rclient, mpi_acl_table_rule) + unloadConfigDB(rclient, depDataMap) + +} + +func TestValidateEditConfig_Update_Syntax_DependentData_Invalid_Op_Seq(t *testing.T) { + + /* ACL and Rule name pre-created . */ + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_CREATE, + "ACL_TABLE|TestACL1", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "781", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err == cvl.CVL_SUCCESS { //Validation should fail + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Update_Syntax_DependentData_Redis_Negative(t *testing.T) { + + /* ACL does not exist.*/ + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "MIRROR_ACTION": "everflow0", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +/* Create with User provided dependent data. */ +func TestValidateEditConfig_Create_Syntax_DependentData_Redis_Positive(t *testing.T) { + + /* ACL and Rule name pre-created . */ + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL22", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + cfgData = []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL22|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvlErrInfo, err = cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +/* Delete Non-Existing Key.*/ +func TestValidateEditConfig_Delete_Semantic_ACLTableReference_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|MyACL11_ACL_IPV4|RULE_1", + map[string]string{}, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Dependent_CacheData(t *testing.T) { + + cvSess, _ := cvl.ValidationSessOpen() + + //Create ACL rule + cfgDataAcl := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL14", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + cvlErrInfo, err1 := cvSess.ValidateEditConfig(cfgDataAcl) + + //Create ACL rule + cfgDataRule := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL14|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvlErrInfo, err2 := cvSess.ValidateEditConfig(cfgDataRule) + + if err1 != cvl.CVL_SUCCESS || err2 != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + cvl.ValidationSessClose(cvSess) +} + +func TestValidateEditConfig_Create_DepData_In_MultiSess(t *testing.T) { + + //Create ACL rule - Session 1 + cvSess, _ := cvl.ValidationSessOpen() + cfgDataAcl := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL16", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + cvlErrInfo, err1 := cvSess.ValidateEditConfig(cfgDataAcl) + + cvl.ValidationSessClose(cvSess) + + //Create ACL rule - Session 2, validation should fail + cvSess, _ = cvl.ValidationSessOpen() + cfgDataRule := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL16|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + _, err2 := cvSess.ValidateEditConfig(cfgDataRule) + + + cvl.ValidationSessClose(cvSess) + + if err1 != cvl.CVL_SUCCESS || err2 == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_DepData_From_Redis_Negative11(t *testing.T) { + + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{}{ + "TestACL1": map[string]interface{}{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + //Create ACL rule - Session 2 + cvSess, _ := cvl.ValidationSessOpen() + cfgDataRule := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL188|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + + cvl.ValidationSessClose(cvSess) + + if err == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + + +func TestValidateEditConfig_Create_DepData_From_Redis(t *testing.T) { + + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{}{ + "TestACL1": map[string]interface{}{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + //Create ACL rule - Session 2 + cvSess, _ := cvl.ValidationSessOpen() + cfgDataRule := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Range_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan701", + map[string]string{ + "vlanid": "7001", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + /* Compare expected error details and error tag. */ + if compareErrorDetails(cvlErrInfo, cvl.CVL_SYNTAX_ERROR, "vlanid-invalid", "") != true { + t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) + } + +} + +func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Length_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL1", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + "policy_desc": "A12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + /* Compare expected error details and error tag. */ + if compareErrorDetails(cvlErrInfo, cvl.CVL_SYNTAX_ERROR, "policy-desc-invalid-length", "") != true { + t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) + } + +} + +func TestValidateEditConfig_Create_Syntax_ErrAppTag_In_Pattern_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan5001", + map[string]string{ + "vlanid": "102", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + /* Compare expected error details and error tag. */ + if compareErrorDetails(cvlErrInfo, cvl.CVL_SYNTAX_ERROR, "vlan-name-invalid", "") != true { + t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) + } + +} + +func TestValidateEditConfig_Create_ErrAppTag_In_Must_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "102", + "members@": "Ethernet24,Ethernet8", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if retCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v %v", cvlErrInfo, retCode) + } + +} + +/* API to test edit config with valid syntax. */ +func TestValidateEditConfig_Create_Syntax_InValid_FieldValue(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_UPDATE, + "ACL_TABLE|TestACL1", + map[string]string{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_NONE, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|TestACL1", + map[string]string{}, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if retCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +/* +//EditConfig(Create) with dependent data from redis +func TestValidateEditConfig_Create_DepData_From_Redis_Negative(t *testing.T) { + + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cfgDataRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL2|Rule1", + map[string]string { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataRule) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { //should not succeed + t.Errorf("Config Validation should fail.") + } + + unloadConfigDB(rclient, depDataMap) +} +*/ + +// EditConfig(Create) with chained leafref from redis +func TestValidateEditConfig_Create_Chained_Leafref_DepData_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan100": map[string]interface{} { + "members@": "Ethernet1", + "vlanid": "100", + }, + }, + "PORT" : map[string]interface{} { + "Ethernet1" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet2" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@":"Ethernet2", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN_MEMBER|Vlan100|Ethernet1", + map[string]string { + "tagging_mode" : "tagged", + }, + }, + } + + _, err := cvSess.ValidateEditConfig(cfgDataVlan) + + if err != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + return + } + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + + _, err = cvSess.ValidateEditConfig(cfgDataAclRule) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) +} + +//EditConfig(Delete) deleting entry already used by other table as leafref +func TestValidateEditConfig_Delete_Dep_Leafref_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + "ACL_RULE": map[string]interface{} { + "TestACL1|Rule1": map[string] interface{} { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_TABLE|TestACL1", + map[string]string { + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataVlan) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err == cvl.CVL_SUCCESS { //should be semantic failure + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) +} + +//EditConfig(Create) with chained leafref from redis +func TestValidateEditConfig_Create_Chained_Leafref_DepData_Negative(t *testing.T) { + depDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet5" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@":"Ethernet2", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + _, err := cvSess.ValidateEditConfig(cfgDataAclRule) + + cvl.ValidationSessClose(cvSess) + + if err == cvl.CVL_SUCCESS { //should not succeed + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) +} +func TestValidateEditConfig_Create_Syntax_InvalidVlanRange_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan5002", + map[string]string{ + "vlanid": "6002", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, retCode := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if retCode == cvl.CVL_SUCCESS { //should not succeed + t.Errorf("Config Validation failed with details %v.", cvlErrInfo) + } + +} + +//Test Initialize() API +func TestLogging(t *testing.T) { + ret := cvl.Initialize() + str := "Testing" + cvl.CVL_LOG(INFO ,"This is Info Log %s", str) + cvl.CVL_LOG(WARNING,"This is Warning Log %s", str) + cvl.CVL_LOG(ERROR ,"This is Error Log %s", str) + cvl.CVL_LOG(INFO_API ,"This is Info API %s", str) + cvl.CVL_LOG(INFO_TRACE ,"This is Info Trace %s", str) + cvl.CVL_LOG(INFO_DEBUG ,"This is Info Debug %s", str) + cvl.CVL_LOG(INFO_DATA ,"This is Info Data %s", str) + cvl.CVL_LOG(INFO_DETAIL ,"This is Info Detail %s", str) + cvl.CVL_LOG(INFO_ALL ,"This is Info all %s", str) + + if (ret != cvl.CVL_SUCCESS) { + t.Errorf("CVl initialization failed") + } + + cvl.Finish() + + //Initialize again for other test cases to run + cvl.Initialize() +} + +func TestValidateEditConfig_DepData_Through_Cache(t *testing.T) { + depDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet5" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + //Modify entry + modDepDataMap := map[string]interface{} { + "PORT" : map[string]interface{} { + "Ethernet3" : map[string]interface{} { + "mtu": "9200", + }, + }, + } + + loadConfigDB(rclient, modDepDataMap) + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL1", + map[string]string { + "stage": "INGRESS", + "type": "L3", + "ports@":"Ethernet3,Ethernet5", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + _, err := cvSess.ValidateEditConfig(cfgDataAclRule) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) + unloadConfigDB(rclient, modDepDataMap) +} + +/* Delete field for an existing key.*/ +func TestValidateEditConfig_Delete_Single_Field_Positive(t *testing.T) { + + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "policy_desc":"Test ACL desc", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_TABLE|TestACL1", + map[string]string{ + "policy_desc":"Test ACL desc", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateConfig_Repeated_Keys_Positive(t *testing.T) { + jsonData := `{ + "WRED_PROFILE": { + "AZURE_LOSSLESS": { + "red_max_threshold": "312000", + "wred_green_enable": "true", + "ecn": "ecn_all", + "green_min_threshold": "104000", + "red_min_threshold": "104000", + "wred_yellow_enable": "true", + "yellow_min_threshold": "104000", + "wred_red_enable": "true", + "yellow_max_threshold": "312000", + "green_max_threshold": "312000" + } + }, + "SCHEDULER": { + "scheduler.0": { + "type": "DWRR", + "weight": "25" + }, + "scheduler.1": { + "type": "DWRR", + "weight": "30" + }, + "scheduler.2": { + "type": "DWRR", + "weight": "20" + } + }, + "QUEUE": { + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|0": { + "scheduler": "[SCHEDULER|scheduler.1]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|1": { + "scheduler": "[SCHEDULER|scheduler.2]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|3-4": { + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]", + "scheduler": "[SCHEDULER|scheduler.0]" + } + } + }` + + cvSess, _ := cvl.ValidationSessOpen() + err := cvSess.ValidateConfig(jsonData) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details.") + } + + cvl.ValidationSessClose(cvSess) +} + +func TestValidateEditConfig_Delete_Entry_Then_Dep_Leafref_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan20": map[string] interface{} { + "vlanid": "20", + }, + }, + "VLAN_MEMBER": map[string]interface{} { + "Vlan20|Ethernet4": map[string] interface{} { + "tagging_mode": "tagged", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataAcl := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "VLAN_MEMBER|Vlan20|Ethernet4", + map[string]string { + }, + }, + } + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataAcl) + + cfgDataAcl = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_DELETE, + "VLAN_MEMBER|Vlan20|Ethernet4", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "VLAN|Vlan20", + map[string]string { + }, + }, + } + + cvlErrInfo, err = cvSess.ValidateEditConfig(cfgDataAcl) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err != cvl.CVL_SUCCESS { //should be success + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) +} + +/* +func TestBadSchema(t *testing.T) { + env := os.Environ() + env[0] = env[0] + " " + + if _, err := os.Stat("/usr/sbin/schema"); os.IsNotExist(err) { + //Corrupt some schema file + 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("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 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" + + " && /bin/sed -i '1 a ' /usr/sbin/schema/sonic-port.yin.bad").Output() + + //Parse bad schema file + if module, _ := yparser.ParseSchemaFile("/usr/sbin/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 /usr/sbin/schema/sonic-port.yin.bad").Output() + } + +} +*/ + +/* +func TestServicability_Debug_Trace(t *testing.T) { + + cvl.Debug(false) + SetTrace(false) + + //Reload the config file by sending SIGUSR2 to ourself + p, err := os.FindProcess(os.Getpid()) + if (err == nil) { + p.Signal(syscall.SIGUSR2) + } + + + depDataMap := map[string]interface{}{ + "ACL_TABLE": map[string]interface{}{ + "TestACL1": map[string]interface{}{ + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + //Create ACL rule - Session 2 + cvSess, _ := cvl.ValidationSessOpen() + cfgDataRule := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + + cvSess.ValidateEditConfig(cfgDataRule) + + unloadConfigDB(rclient, depDataMap) + + SetTrace(true) + cvl.Debug(true) + + cvl.ValidationSessClose(cvSess) + + //Reload the bad config file by sending SIGUSR2 to ourself + exec.Command("/bin/sh", "-c", "/bin/cp conf/cvl_cfg.json conf/cvl_cfg.json.orig" + + " && /bin/echo 'junk' >> conf/cvl_cfg.json").Output() + p, err = os.FindProcess(os.Getpid()) + if (err == nil) { + p.Signal(syscall.SIGUSR2) + } + exec.Command("/bin/sh", "-c", "/bin/mv conf/cvl_cfg.json.orig conf/cvl_cfg.json").Output() + p.Signal(syscall.SIGUSR2) +}*/ + +// EditConfig(Create) with chained leafref from redis +func TestValidateEditConfig_Delete_Create_Same_Entry_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan100": map[string]interface{} { + "members@": "Ethernet1", + "vlanid": "100", + }, + }, + "PORT" : map[string]interface{} { + "Ethernet1" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "VLAN|Vlan100", + map[string]string { + }, + }, + } + + _, err1 := cvSess.ValidateEditConfig(cfgDataVlan) + + //Same entry getting created again + cfgDataVlan = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan100", + map[string]string { + "vlanid": "100", + }, + }, + } + + _, err2 := cvSess.ValidateEditConfig(cfgDataVlan) + + if err1 != cvl.CVL_SUCCESS || err2 != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + return + } + + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMap) +} + +func TestValidateStartupConfig_Positive(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + if cvl.CVL_NOT_IMPLEMENTED != cvSess.ValidateStartupConfig("") { + t.Errorf("Not implemented yet.") + } + cvl.ValidationSessClose(cvSess) +} + +func TestValidateIncrementalConfig_Positive(t *testing.T) { + existingDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan800": map[string]interface{} { + "members@": "Ethernet1", + "vlanid": "800", + }, + "Vlan801": map[string]interface{} { + "members@": "Ethernet2", + "vlanid": "801", + }, + }, + "VLAN_MEMBER": map[string]interface{} { + "Vlan800|Ethernet1": map[string] interface{} { + "tagging_mode": "tagged", + }, + }, + "PORT" : map[string]interface{} { + "Ethernet1" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "81,82,83,84", + "mtu": "9100", + }, + "Ethernet2" : map[string]interface{} { + "alias":"hundredGigE1", + "lanes": "85,86,87,89", + "mtu": "9100", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, existingDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + jsonData := `{ + "VLAN": { + "Vlan800": { + "members": [ + "Ethernet1", + "Ethernet2" + ], + "vlanid": "800" + } + }, + "VLAN_MEMBER": { + "Vlan800|Ethernet1": { + "tagging_mode": "untagged" + }, + "Vlan801|Ethernet2": { + "tagging_mode": "tagged" + } + } + }` + + ret := cvSess.ValidateIncrementalConfig(jsonData) + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, existingDataMap) + + if ret != cvl.CVL_SUCCESS { //should succeed + t.Errorf("Config Validation failed.") + return + } +} + +//Validate key only +func TestValidateKeys(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + if cvl.CVL_NOT_IMPLEMENTED != cvSess.ValidateKeys([]string{}) { + t.Errorf("Not implemented yet.") + } + cvl.ValidationSessClose(cvSess) +} + +//Validate key and data +func TestValidateKeyData(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + if cvl.CVL_NOT_IMPLEMENTED != cvSess.ValidateKeyData("", "") { + t.Errorf("Not implemented yet.") + } + cvl.ValidationSessClose(cvSess) +} + +//Validate key, field and value +func TestValidateFields(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + if cvl.CVL_NOT_IMPLEMENTED != cvSess.ValidateFields("", "", "") { + t.Errorf("Not implemented yet.") + } + cvl.ValidationSessClose(cvSess) +} + +func TestValidateEditConfig_Two_Updates_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataAcl := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_TABLE|TestACL1", + map[string]string { + "policy_desc": "Test ACL", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_TABLE|TestACL1", + map[string]string { + "type": "MIRROR", + }, + }, + } + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgDataAcl) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err != cvl.CVL_SUCCESS { //should be success + t.Errorf("Config Validation failed.") + } + + unloadConfigDB(rclient, depDataMap) + +} +func TestValidateEditConfig_Create_Syntax_DependentData_PositivePortChannel(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "1001", + "members@": "Ethernet28,PortChannel002", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + + +func TestValidateEditConfig_Create_Syntax_DependentData_PositivePortChannelIfName(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "1001", + "members@": "Ethernet24,PortChannel001", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, err := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if err != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelEthernet(t *testing.T) { + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "1001", + "members@": "PortChannel001,Ethernet4", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Create_Syntax_DependentData_NegativePortChannelNew(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string{ + "vlanid": "1001", + "members@": "Ethernet12,PortChannel001", + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan201": map[string] interface{} { + "vlanid": "201", + "members@": "Ethernet8", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "VLAN|Vlan201", + map[string]string{ + "members@": "Ethernet8,Ethernet12", + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + unloadConfigDB(rclient, depDataMap) + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + return + } + + cfgData = []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN_MEMBER|Vlan201|Ethernet8", + map[string]string{ + "tagging_mode": "tagged", + }, + }, + } + + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMap) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Use_Updated_Data_As_Create_DependentData_Single_Call_Positive(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN" : map[string]interface{} { + "Vlan201": map[string] interface{} { + "vlanid": "201", + "members@": "Ethernet8", + }, + }, + } + + //Prepare data in Redis + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "VLAN|Vlan201", + map[string]string{ + "members@": "Ethernet8,Ethernet12", + }, + }, + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN_MEMBER|Vlan201|Ethernet8", + map[string]string{ + "tagging_mode": "tagged", + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMap) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Create_Syntax_Interface_AllKeys_Positive(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "INTERFACE|Ethernet24|10.0.0.0/31", + map[string]string{ + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Create_Syntax_Interface_OptionalKey_Positive(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "INTERFACE|Ethernet24", + map[string]string{ + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_Create_Syntax_Interface_IncorrectKey_Negative(t *testing.T) { + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "INTERFACE|10.0.0.0/31", + map[string]string{ + }, + }, + } + + cvSess, _ := cvl.ValidationSessOpen() + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } +} + +func TestValidateEditConfig_EmptyNode_Positive(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "PORT|Ethernet0", + map[string]string{ + "description": "", + "index": "3", + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + cvl.ValidationSessClose(cvSess) + + WriteToFile(fmt.Sprintf("\nCVL Error Info is %v\n", cvlErrInfo)) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestSortDepTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.SortDepTables([]string{"PORT", "ACL_RULE", "ACL_TABLE"}) + + expectedResult := []string{"ACL_RULE", "ACL_TABLE", "PORT"} + + if len(expectedResult) != len(result) { + t.Errorf("Validation failed, returned value = %v", result) + return + } + + for i := 0; i < len(expectedResult) ; i++ { + if result[i] != expectedResult[i] { + t.Errorf("Validation failed, returned value = %v", result) + break + } + } + + cvl.ValidationSessClose(cvSess) +} + +func TestGetOrderedTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.GetOrderedTables("sonic-vlan") + + expectedResult := []string{"VLAN_MEMBER", "VLAN"} + + if len(expectedResult) != len(result) { + t.Errorf("Validation failed, returned value = %v", result) + return + } + + for i := 0; i < len(expectedResult) ; i++ { + if result[i] != expectedResult[i] { + t.Errorf("Validation failed, returned value = %v", result) + break + } + } + + cvl.ValidationSessClose(cvSess) +} + +func TestGetDepTables(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + result, _ := cvSess.GetDepTables("sonic-acl", "ACL_RULE") + + expectedResult := []string{"ACL_RULE", "ACL_TABLE", "MIRROR_SESSION", "PORT"} + expectedResult1 := []string{"ACL_RULE", "MIRROR_SESSION", "ACL_TABLE", "PORT"} //2nd possible result + + if len(expectedResult) != len(result) { + t.Errorf("Validation failed, returned value = %v", result) + return + } + + for i := 0; i < len(expectedResult) ; i++ { + if result[i] != expectedResult[i] && result[i] != expectedResult1[i] { + t.Errorf("Validation failed, returned value = %v", result) + break + } + } + + cvl.ValidationSessClose(cvSess) +} + + +func TestGetDepDataForDelete(t *testing.T) { + depDataMap := map[string]interface{} { + "VLAN_MEMBER" : map[string]interface{} { + "Vlan21|Ethernet7": map[string] interface{} { + "tagging_mode": "tagged", + }, + "Vlan22|Ethernet7": map[string] interface{} { + "tagging_mode": "tagged", + }, + }, + "PORTCHANNEL_MEMBER" : map[string]interface{} { + "Ch47|Ethernet7": map[string] interface{} { + "NULL": "NULL", + }, + }, + "ACL_TABLE" : map[string]interface{} { + "TestACL1": map[string] interface{} { + "stage": "INGRESS", + "type": "L3", + "ports@": "Ethernet7,Ethernet9", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + depKeys, depKeysMod := cvSess.GetDepDataForDelete("PORT|Ethernet7") + + + cvl.ValidationSessClose(cvSess) + + if (len(depKeys) == 0) || (len(depKeysMod) == 0) { + t.Errorf("GetDepDataForDelete() failed") + } + + unloadConfigDB(rclient, depDataMap) +} + +func TestMaxElements_All_Entries_In_Request(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP|GLOBAL", + map[string]string{ + "mode": "pvst", + }, + }, + } + + //Add first element + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + /* + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + */ + + cfgData1 := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP|GLOBAL1", + map[string]string{ + "mode": "mstp", + }, + }, + } + + //Try to add second element + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgData1) + + cvl.ValidationSessClose(cvSess) + + //Should fail as "DEVICE_METADATA" has max-elements as '1' + if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestMaxElements_Entries_In_Redis(t *testing.T) { + depDataMap := map[string]interface{} { + "STP": map[string]interface{}{ + "GLOBAL": map[string]interface{}{ + "mode": "pvst", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgData := []cvl.CVLEditConfigData{ + cvl.CVLEditConfigData{ + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP|GLOBAL1", + map[string]string{ + "mode": "mstp", + }, + }, + } + + //Try to add second element + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgData) + + unloadConfigDB(rclient, depDataMap) + + cvl.ValidationSessClose(cvSess) + + //Should fail as "DEVICE_METADATA" has max-elements as '1' + if cvlErrInfo.ErrCode == cvl.CVL_SUCCESS { + t.Errorf("Config Validation failed -- error details %v", cvlErrInfo) + } + +} + +func TestValidateEditConfig_Two_Create_Requests_Positive(t *testing.T) { + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan21", + map[string]string { + "vlanid": "21", + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataVlan) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + cvl.ValidationSessClose(cvSess) + t.Errorf("VLAN Create : Config Validation failed") + return + } + + cfgDataVlan = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_CREATE, + "VLAN|Vlan21", + map[string]string { + "vlanid": "21", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "STP_VLAN|Vlan21", + map[string]string { + "enabled": "true", + "forward_delay": "15", + "hello_time": "2", + "max_age" : "20", + "priority": "327", + "vlanid": "21", + }, + }, + } + + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataVlan) + + cvl.ValidationSessClose(cvSess) + + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("STP VLAN Create : Config Validation failed") + return + } +} + +func TestValidateEditConfig_Two_Delete_Requests_Positive(t *testing.T) { + depDataMap := map[string]interface{}{ + "VLAN": map[string]interface{}{ + "Vlan51": map[string]interface{}{ + "vlanid": "51", + }, + }, + "STP_VLAN": map[string]interface{}{ + "Vlan51": map[string]interface{}{ + "enabled": "true", + "forward_delay": "15", + "hello_time": "2", + "max_age" : "20", + "priority": "327", + "vlanid": "51", + }, + }, + } + + loadConfigDB(rclient, depDataMap) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "STP_VLAN|Vlan51", + map[string]string { + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataVlan) + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + cvl.ValidationSessClose(cvSess) + unloadConfigDB(rclient, depDataMap) + t.Errorf("STP VLAN delete : Config Validation failed") + return + } + + cfgDataVlan = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_DELETE, + "STP_VLAN|Vlan51", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "VLAN|Vlan51", + map[string]string { + }, + }, + } + + cvlErrInfo, _ = cvSess.ValidateEditConfig(cfgDataVlan) + if cvlErrInfo.ErrCode != cvl.CVL_SUCCESS { + t.Errorf("VLAN delete : Config Validation failed") + } + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMap) +} + +func TestVailidateStaticPlatformLimits_YANG_Deviation_Ngeative(t *testing.T) { + + //Get platform + platformName := "" + metaData, err:= rclient.HGetAll("DEVICE_METADATA|localhost").Result() + + if (err == nil) { + platformName, _ = metaData["platform"] + } + + depDataMapAcl := map[string]interface{}{ + "ACL_TABLE": map[string]interface{}{ + "TestACL901": map[string]interface{}{ + "type": "L3", + }, + "TestACL902": map[string]interface{}{ + "type": "L3", + }, + }, + } + + loadConfigDB(rclient, depDataMapAcl) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataAcl := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL903", + map[string]string { + "type": "L3", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_TABLE|TestACL904", + map[string]string { + "type": "L3", + }, + }, + } + + cvlErrInfo, _ := cvSess.ValidateEditConfig(cfgDataAcl) + + if (strings.Contains(platformName, "quanta_ix8")) && + (cvlErrInfo.ErrCode == cvl.CVL_SUCCESS) { + t.Errorf("Should not be able to create more than 3 ACL TABLEs") + } + + cvl.ValidationSessClose(cvSess) + + unloadConfigDB(rclient, depDataMapAcl) +} + diff --git a/src/cvl/internal/util/util.go b/src/cvl/internal/util/util.go new file mode 100644 index 0000000000..f82c1c3943 --- /dev/null +++ b/src/cvl/internal/util/util.go @@ -0,0 +1,422 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 + +/* +#cgo LDFLAGS: -lyang +#include + +extern void customLogCallback(LY_LOG_LEVEL, char* msg, char* path); + +static void customLogCb(LY_LOG_LEVEL level, const char* msg, const char* path) { + customLogCallback(level, (char*)msg, (char*)path); +} + +static void ly_set_log_callback(int enable) { + if (enable == 1) { + ly_verb(LY_LLDBG); + ly_set_log_clb(customLogCb, 0); + } else { + ly_verb(LY_LLERR); + ly_set_log_clb(NULL, 0); + } +} + +*/ +import "C" +import ( + "os" + "fmt" + "io" + "runtime" + "encoding/json" + "io/ioutil" + "os/signal" + "syscall" + "strings" + "flag" + "strconv" + log "github.com/golang/glog" + fileLog "log" + "sync" +) + +var CVL_SCHEMA string = "schema/" +var CVL_CFG_FILE string = "/usr/sbin/cvl_cfg.json" +const CVL_LOG_FILE = "/tmp/cvl.log" + +//package init function +func init() { + if (os.Getenv("CVL_SCHEMA_PATH") != "") { + CVL_SCHEMA = os.Getenv("CVL_SCHEMA_PATH") + "/" + } + + if (os.Getenv("CVL_CFG_FILE") != "") { + CVL_CFG_FILE = os.Getenv("CVL_CFG_FILE") + } + + //Initialize mutex + logFileMutex = &sync.Mutex{} +} + +var cvlCfgMap map[string]string +var isLogToFile bool +var logFileSize int +var pLogFile *os.File +var logFileMutex *sync.Mutex + +/* Logging Level for CVL global logging. */ +type CVLLogLevel uint8 +const ( + INFO = 0 + iota + WARNING + ERROR + FATAL + INFO_DEBUG + INFO_API + INFO_DATA + INFO_DETAIL + INFO_TRACE + INFO_ALL +) + +var cvlTraceFlags uint32 + +/* Logging levels for CVL Tracing. */ +type CVLTraceLevel uint32 +const ( + TRACE_MIN = 0 + TRACE_MAX = 8 + TRACE_CACHE = 1 << TRACE_MIN + TRACE_LIBYANG = 1 << 1 + TRACE_YPARSER = 1 << 2 + TRACE_CREATE = 1 << 3 + TRACE_UPDATE = 1 << 4 + TRACE_DELETE = 1 << 5 + TRACE_SEMANTIC = 1 << 6 + TRACE_ONERROR = 1 << 7 + TRACE_SYNTAX = 1 << TRACE_MAX + +) + + +var traceLevelMap = map[int]string { + /* Caching operation traces */ + TRACE_CACHE : "TRACE_CACHE", + /* Libyang library traces. */ + TRACE_LIBYANG: "TRACE_LIBYANG", + /* Yang Parser traces. */ + TRACE_YPARSER : "TRACE_YPARSER", + /* Create operation traces. */ + TRACE_CREATE : "TRACE_CREATE", + /* Update operation traces. */ + TRACE_UPDATE : "TRACE_UPDATE", + /* Delete operation traces. */ + TRACE_DELETE : "TRACE_DELETE", + /* Semantic Validation traces. */ + TRACE_SEMANTIC : "TRACE_SEMANTIC", + /* Syntax Validation traces. */ + TRACE_SYNTAX : "TRACE_SYNTAX", + /* Trace on Error. */ + TRACE_ONERROR : "TRACE_ONERROR", +} + +var Tracing bool = false + +var traceFlags uint16 = 0 + +func SetTrace(on bool) { + if (on == true) { + Tracing = true + traceFlags = 1 + } else { + Tracing = false + traceFlags = 0 + } +} + +func IsTraceSet() bool { + if (traceFlags == 0) { + return false + } else { + return true + } +} + +/* The following function enbles the libyang logging by +changing libyang's global log setting */ + +//export customLogCallback +func customLogCallback(level C.LY_LOG_LEVEL, msg *C.char, path *C.char) { + TRACE_LEVEL_LOG(TRACE_YPARSER, "[libyang] %s (path: %s)", + C.GoString(msg), C.GoString(path)) +} + +func TRACE_LEVEL_LOG(tracelevel CVLTraceLevel, fmtStr string, args ...interface{}) { + + /* + if (IsTraceSet() == false) { + return + } + + level = (level - INFO_API) + 1; + */ + + traceEnabled := false + if ((cvlTraceFlags & (uint32)(tracelevel)) != 0) { + traceEnabled = true + } + if (traceEnabled == true) && (isLogToFile == true) { + logToCvlFile(fmtStr, args...) + return + } + + if IsTraceSet() == true && traceEnabled == true { + pc := make([]uintptr, 10) + runtime.Callers(2, pc) + f := runtime.FuncForPC(pc[0]) + file, line := f.FileLine(pc[0]) + + fmt.Printf("%s:%d [CVL] : %s(): ", file, line, f.Name()) + fmt.Printf(fmtStr+"\n", args...) + } else { + fmtStr = "[CVL] : " + fmtStr + if (traceEnabled == true) { + //Trace logs has verbose level INFO_TRACE + log.V(INFO_TRACE).Infof(fmtStr, args...) + } + } +} + +//Logs to /tmp/cvl.log file +func logToCvlFile(format string, args ...interface{}) { + if (pLogFile == nil) { + return + } + + logFileMutex.Lock() + if (logFileSize == 0) { + fileLog.Printf(format, args...) + logFileMutex.Unlock() + return + } + + fStat, err := pLogFile.Stat() + + var curSize int64 = 0 + if (err == nil) && (fStat != nil) { + curSize = fStat.Size() + } + + // Roll over the file contents if size execeeds max defined limit + if (curSize >= int64(logFileSize)) { + //Write 70% contents from bottom and write to top + //Truncate 30% of bottom + + //close the file first + pLogFile.Close() + + pFile, err := os.OpenFile(CVL_LOG_FILE, + os.O_RDONLY, 0666) + pFileOut, errOut := os.OpenFile(CVL_LOG_FILE + ".tmp", + os.O_WRONLY | os.O_CREATE, 0666) + + + if (err != nil) && (errOut != nil) { + fileLog.Printf("Failed to roll over the file, current size %v", curSize) + } else { + pFile.Seek(int64(logFileSize * 30/100), io.SeekStart) + _, err := io.Copy(pFileOut, pFile) + if err == nil { + os.Rename(CVL_LOG_FILE + ".tmp", CVL_LOG_FILE) + } + } + + if (pFile != nil) { + pFile.Close() + } + if (pFileOut != nil) { + pFileOut.Close() + } + + // Reopen the file + pLogFile, err := os.OpenFile(CVL_LOG_FILE, + os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) + if err != nil { + fmt.Printf("Error in opening log file %s, %v", CVL_LOG_FILE, err) + } else { + fileLog.SetOutput(pLogFile) + } + } + + + fileLog.Printf(format, args...) + + logFileMutex.Unlock() +} + +func CVL_LEVEL_LOG(level CVLLogLevel, format string, args ...interface{}) { + + if (isLogToFile == true) { + logToCvlFile(format, args...) + return + } + + format = "[CVL] : " + format + + switch level { + case INFO: + log.Infof(format, args...) + case WARNING: + log.Warningf(format, args...) + case ERROR: + log.Errorf(format, args...) + case FATAL: + log.Fatalf(format, args...) + case INFO_API: + log.V(1).Infof(format, args...) + case INFO_TRACE: + log.V(2).Infof(format, args...) + case INFO_DEBUG: + log.V(3).Infof(format, args...) + case INFO_DATA: + log.V(4).Infof(format, args...) + case INFO_DETAIL: + log.V(5).Infof(format, args...) + case INFO_ALL: + log.V(6).Infof(format, args...) + } + +} + +// Function to check CVL log file related settings +func applyCvlLogFileConfig() { + + if (pLogFile != nil) { + pLogFile.Close() + pLogFile = nil + } + + //Disable libyang trace log + C.ly_set_log_callback(0) + isLogToFile = false + logFileSize = 0 + + enabled, exists := cvlCfgMap["LOG_TO_FILE"] + if exists == false { + return + } + + if fileSize, sizeExists := cvlCfgMap["LOG_FILE_SIZE"]; + sizeExists == true { + logFileSize, _ = strconv.Atoi(fileSize) + } + + if (enabled == "true") { + pFile, err := os.OpenFile(CVL_LOG_FILE, + os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666) + + if err != nil { + fmt.Printf("Error in opening log file %s, %v", CVL_LOG_FILE, err) + } else { + pLogFile = pFile + fileLog.SetOutput(pLogFile) + isLogToFile = true + } + + //Enable libyang trace log + C.ly_set_log_callback(1) + } +} + +func ConfigFileSyncHandler() { + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGUSR2) + go func() { + for { + <-sigs + cvlCfgMap := ReadConfFile() + + if cvlCfgMap == nil { + return + } + + CVL_LEVEL_LOG(INFO ,"Received SIGUSR2. Changed configuration values are %v", cvlCfgMap) + + + flag.Set("v", cvlCfgMap["VERBOSITY"]) + if (strings.Compare(cvlCfgMap["LOGTOSTDERR"], "true") == 0) { + SetTrace(true) + flag.Set("logtostderr", "true") + flag.Set("stderrthreshold", cvlCfgMap["STDERRTHRESHOLD"]) + } + + } + }() + +} + +func ReadConfFile() map[string]string{ + + /* Return if CVL configuration file is not present. */ + if _, err := os.Stat(CVL_CFG_FILE); os.IsNotExist(err) { + return nil + } + + data, err := ioutil.ReadFile(CVL_CFG_FILE) + + err = json.Unmarshal(data, &cvlCfgMap) + + if err != nil { + CVL_LEVEL_LOG(INFO ,"Error in reading cvl configuration file %v", err) + return nil + } + + CVL_LEVEL_LOG(INFO ,"Current Values of CVL Configuration File %v", cvlCfgMap) + var index uint32 + + for index = TRACE_MIN ; index <= TRACE_MAX ; index++ { + if (strings.Compare(cvlCfgMap[traceLevelMap[1 << index]], "true") == 0) { + cvlTraceFlags = cvlTraceFlags | (1 << index) + } + } + + applyCvlLogFileConfig() + + return cvlCfgMap +} + +func SkipValidation() bool { + val, existing := cvlCfgMap["SKIP_VALIDATION"] + if (existing == true) && (val == "true") { + return true + } + + return false +} + +func SkipSemanticValidation() bool { + val, existing := cvlCfgMap["SKIP_SEMANTIC_VALIDATION"] + if (existing == true) && (val == "true") { + return true + } + + return false +} diff --git a/src/cvl/internal/yparser/yparser.go b/src/cvl/internal/yparser/yparser.go new file mode 100644 index 0000000000..c4968a88de --- /dev/null +++ b/src/cvl/internal/yparser/yparser.go @@ -0,0 +1,915 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 */ + +import ( + "os" + "fmt" + "strings" + . "cvl/internal/util" + "unsafe" +) + +/* +#cgo LDFLAGS: -lyang +#include +#include +#include +#include +#include + +struct lyd_node* lyd_parse_data_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options) { + return lyd_parse_path(ctx, path, format, options); +} + +struct lyd_node *lyd_parse_data_mem(struct ly_ctx *ctx, const char *data, LYD_FORMAT format, int options) +{ + return lyd_parse_mem(ctx, data, format, options); +} + +int lyd_data_validate(struct lyd_node **node, int options, struct ly_ctx *ctx) +{ + return lyd_validate(node, options, ctx); +} + +int lyd_data_validate_all(const char *data, const char *depData, const char *othDepData, int options, struct ly_ctx *ctx) +{ + struct lyd_node *pData; + struct lyd_node *pDepData; + struct lyd_node *pOthDepData; + + if ((data == NULL) || (data[0] == '\0')) + { + return -1; + } + + pData = lyd_parse_mem(ctx, data, LYD_XML, LYD_OPT_EDIT | LYD_OPT_NOEXTDEPS); + if (pData == NULL) + { + return -1; + } + + if ((depData != NULL) && (depData[0] != '\0')) + { + if (NULL != (pDepData = lyd_parse_mem(ctx, depData, LYD_XML, LYD_OPT_EDIT | LYD_OPT_NOEXTDEPS))) + { + if (0 != lyd_merge_to_ctx(&pData, pDepData, LYD_OPT_DESTRUCT, ctx)) + { + return -1; + } + } + } + + if ((othDepData != NULL) && (othDepData[0] != '\0')) + { + if (NULL != (pOthDepData = lyd_parse_mem(ctx, othDepData, LYD_XML, LYD_OPT_EDIT | LYD_OPT_NOEXTDEPS))) + { + if (0 != lyd_merge_to_ctx(&pData, pOthDepData, LYD_OPT_DESTRUCT, ctx)) + { + return -1; + } + } + } + + return lyd_validate(&pData, LYD_OPT_CONFIG, ctx); +} + +int lyd_multi_new_leaf(struct lyd_node *parent, const struct lys_module *module, const char *leafVal) +{ + char s[4048]; + char *name, *val; + char *saveptr; + + strcpy(s, leafVal); + + name = strtok_r(s, "#", &saveptr); + + while (name != NULL) + { + val = strtok_r(NULL, "#", &saveptr); + if (val != NULL) + { + if (NULL == lyd_new_leaf(parent, module, name, val)) + { + return -1; + } + } + + name = strtok_r(NULL, "#", &saveptr); + } +} + +struct lyd_node *lyd_find_node(struct lyd_node *root, const char *xpath) +{ + struct ly_set *set = NULL; + struct lyd_node *node = NULL; + + if (root == NULL) + { + return NULL; + } + + set = lyd_find_path(root, xpath); + if (set == NULL || set->number == 0) { + return NULL; + } + + node = set->set.d[0]; + ly_set_free(set); + + return node; +} + +struct lys_node* lys_get_snode(struct ly_set *set, int idx) { + if (set == NULL || set->number == 0) { + return NULL; + } + + return set->set.s[idx]; +} + +int lyd_change_leaf_data(struct lyd_node *leaf, const char *val_str) { + return lyd_change_leaf((struct lyd_node_leaf_list *)leaf, val_str); +} + +struct lys_leaf_ref_path { + const char *path[10]; //max 10 path + int count; //actual path count +}; + +struct lys_leaf_ref_path* lys_get_leafrefs(struct lys_node_leaf *node) { + static struct lys_leaf_ref_path leafrefs; + memset(&leafrefs, 0, sizeof(leafrefs)); + + if (node->type.base == LY_TYPE_LEAFREF) { + leafrefs.path[0] = node->type.info.lref.path; + leafrefs.count = 1; + + } else if (node->type.base == LY_TYPE_UNION) { + for (int typeCnt = 0; typeCnt < node->type.info.uni.count; typeCnt++) { + if (node->type.info.uni.types[typeCnt].base != LY_TYPE_LEAFREF) { + continue; + } + + leafrefs.path[leafrefs.count] = node->type.info.uni.types[typeCnt].info.lref.path; + leafrefs.count += 1; + } + } + + if (leafrefs.count > 0) { + return &leafrefs; + } else { + return NULL; + } +} + +*/ +import "C" + +type YParserCtx C.struct_ly_ctx +type YParserNode C.struct_lyd_node +type YParserSNode C.struct_lys_node +type YParserModule C.struct_lys_module + +var ypCtx *YParserCtx +var ypOpModule *YParserModule +var ypOpRoot *YParserNode //Operation root +var ypOpNode *YParserNode //Operation node + +type XpathExpression struct { + Expr string + ErrCode string + ErrStr string +} + +//Important schema information to be loaded at bootup time +type YParserListInfo struct { + ListName string + Module *YParserModule + DbName string + ModelName string + RedisTableName string //To which Redis table it belongs to, used for 1 Redis to N Yang List + Keys []string + RedisKeyDelim string + RedisKeyPattern string + RedisTableSize int + MapLeaf []string //for 'mapping list' + LeafRef map[string][]string //for storing all leafrefs for a leaf in a table, + //multiple leafref possible for union + XpathExpr map[string]*XpathExpression + CustValidation map[string]string +} + +type YParser struct { + ctx *YParserCtx //Parser context + root *YParserNode //Top evel root for validation + operation string //Edit operation +} + +/* YParser Error Structure */ +type YParserError struct { + ErrCode YParserRetCode /* Error Code describing type of error. */ + Msg string /* Detailed error message. */ + ErrTxt string /* High level error message. */ + TableName string /* List/Table having error */ + Keys []string /* Keys of the Table having error. */ + Field string /* Field Name throwing error . */ + Value string /* Field Value throwing error */ + ErrAppTag string /* Error App Tag. */ +} + +type YParserRetCode int +const ( + YP_SUCCESS YParserRetCode = 1000 + iota + YP_SYNTAX_ERROR + YP_SEMANTIC_ERROR + YP_SYNTAX_MISSING_FIELD + YP_SYNTAX_INVALID_FIELD /* Invalid Field */ + YP_SYNTAX_INVALID_INPUT_DATA /*Invalid Input Data */ + YP_SYNTAX_MULTIPLE_INSTANCE /* Multiple Field Instances */ + YP_SYNTAX_DUPLICATE /* Duplicate Fields */ + YP_SYNTAX_ENUM_INVALID /* Invalid enum value */ + YP_SYNTAX_ENUM_INVALID_NAME /* Invalid enum name */ + YP_SYNTAX_ENUM_WHITESPACE /* Enum name with leading/trailing whitespaces */ + YP_SYNTAX_OUT_OF_RANGE /* Value out of range/length/pattern (data) */ + YP_SYNTAX_MINIMUM_INVALID /* min-elements constraint not honored */ + YP_SYNTAX_MAXIMUM_INVALID /* max-elements constraint not honored */ + YP_SEMANTIC_DEPENDENT_DATA_MISSING /* Dependent Data is missing */ + YP_SEMANTIC_MANDATORY_DATA_MISSING /* Mandatory Data is missing */ + YP_SEMANTIC_KEY_ALREADY_EXIST /* Key already existing */ + YP_SEMANTIC_KEY_NOT_EXIST /* Key is missing */ + YP_SEMANTIC_KEY_DUPLICATE /* Duplicate key */ + YP_SEMANTIC_KEY_INVALID /* Invalid key */ + YP_INTERNAL_UNKNOWN +) + +const ( + YP_NOP = 1 + iota + YP_OP_CREATE + YP_OP_UPDATE + YP_OP_DELETE +) + +var yparserInitialized bool = false + +func TRACE_LOG(tracelevel CVLTraceLevel, fmtStr string, args ...interface{}) { + TRACE_LEVEL_LOG(tracelevel , fmtStr, args...) +} + +func CVL_LOG(level CVLLogLevel, fmtStr string, args ...interface{}) { + CVL_LEVEL_LOG(level, fmtStr, args...) +} + +//package init function +func init() { + if (os.Getenv("CVL_DEBUG") != "") { + Debug(true) + } +} + +func Debug(on bool) { + if (on == true) { + C.ly_verb(C.LY_LLDBG) + } else { + C.ly_verb(C.LY_LLERR) + } +} + +func Initialize() { + if (yparserInitialized != true) { + ypCtx = (*YParserCtx)(C.ly_ctx_new(C.CString(CVL_SCHEMA), 0)) + C.ly_verb(C.LY_LLERR) + // yparserInitialized = true + } +} + +func Finish() { + if (yparserInitialized == true) { + C.ly_ctx_destroy((*C.struct_ly_ctx)(ypCtx), nil) + // yparserInitialized = false + } +} + +//Parse YIN schema file +func ParseSchemaFile(modelFile string) (*YParserModule, YParserError) { + /* schema */ + TRACE_LOG(TRACE_YPARSER, "Parsing schema file %s ...", modelFile) + + module := C.lys_parse_path((*C.struct_ly_ctx)(ypCtx), C.CString(modelFile), C.LYS_IN_YIN) + if module == nil { + return nil, getErrorDetails() + } + + if (strings.Contains(modelFile, "sonic-common.yin") == true) { + ypOpModule = (*YParserModule)(module) + ypOpRoot = (*YParserNode)(C.lyd_new(nil, (*C.struct_lys_module)(ypOpModule), C.CString("operation"))) + ypOpNode = (*YParserNode)(C.lyd_new_leaf((*C.struct_lyd_node)(ypOpRoot), (*C.struct_lys_module)(ypOpModule), C.CString("operation"), C.CString("NOP"))) + } + + return (*YParserModule)(module), YParserError {ErrCode : YP_SUCCESS,} +} + +//Add child node to a parent node +func(yp *YParser) AddChildNode(module *YParserModule, parent *YParserNode, name string) *YParserNode { + + ret := (*YParserNode)(C.lyd_new((*C.struct_lyd_node)(parent), (*C.struct_lys_module)(module), C.CString(name))) + if (ret == nil) { + TRACE_LOG(TRACE_YPARSER, "Failed parsing node %s", name) + } + + return ret +} + +//Add child node to a parent node +func(yp *YParser) AddMultiLeafNodes(module *YParserModule, parent *YParserNode, multiLeaf string) YParserError { + if (0 != C.lyd_multi_new_leaf((*C.struct_lyd_node)(parent), (*C.struct_lys_module)(module), C.CString(multiLeaf))) { + if (Tracing == true) { + TRACE_LOG(TRACE_ONERROR, "Failed to create Multi Leaf Data = %v", multiLeaf) + } + return getErrorDetails() + } + + return YParserError {ErrCode : YP_SUCCESS,} + +} + +//Return entire subtree in XML format in string +func (yp *YParser) NodeDump(root *YParserNode) string { + if (root == nil) { + return "" + } else { + var outBuf *C.char + C.lyd_print_mem(&outBuf, (*C.struct_lyd_node)(root), C.LYD_XML, C.LYP_WITHSIBLINGS) + return C.GoString(outBuf) + } +} + +//Merge source with destination +func (yp *YParser) MergeSubtree(root, node *YParserNode) (*YParserNode, YParserError) { + rootTmp := (*C.struct_lyd_node)(root) + + if (root == nil || node == nil) { + return root, YParserError {ErrCode: YP_SUCCESS} + } + + if (Tracing == true) { + rootdumpStr := yp.NodeDump((*YParserNode)(rootTmp)) + TRACE_LOG(TRACE_YPARSER, "Root subtree = %v\n", rootdumpStr) + } + + if (0 != C.lyd_merge_to_ctx(&rootTmp, (*C.struct_lyd_node)(node), C.LYD_OPT_DESTRUCT, + (*C.struct_ly_ctx)(ypCtx))) { + return (*YParserNode)(rootTmp), getErrorDetails() + } + + if (Tracing == true) { + dumpStr := yp.NodeDump((*YParserNode)(rootTmp)) + TRACE_LOG(TRACE_YPARSER, "Merged subtree = %v\n", dumpStr) + } + + return (*YParserNode)(rootTmp), YParserError {ErrCode : YP_SUCCESS,} +} + +//Cache subtree +func (yp *YParser) CacheSubtree(dupSrc bool, node *YParserNode) YParserError { + rootTmp := (*C.struct_lyd_node)(yp.root) + var dup *C.struct_lyd_node + + if (node == nil) { + //nothing to merge + return YParserError {ErrCode : YP_SUCCESS,} + } + + if (dupSrc == true) { + dup = C.lyd_dup_withsiblings((*C.struct_lyd_node)(node), C.LYD_DUP_OPT_RECURSIVE | C.LYD_DUP_OPT_NO_ATTR) + } else { + dup = (*C.struct_lyd_node)(node) + } + + if (yp.root != nil) { + if (0 != C.lyd_merge_to_ctx(&rootTmp, (*C.struct_lyd_node)(dup), C.LYD_OPT_DESTRUCT, + (*C.struct_ly_ctx)(ypCtx))) { + return getErrorDetails() + } + } else { + yp.root = (*YParserNode)(dup) + } + + if (Tracing == true) { + dumpStr := yp.NodeDump((*YParserNode)(rootTmp)) + TRACE_LOG(TRACE_YPARSER, "Cached subtree = %v\n", dumpStr) + } + + return YParserError {ErrCode : YP_SUCCESS,} +} + +func (yp *YParser) DestroyCache() YParserError { + + if (yp.root != nil) { + C.lyd_free_withsiblings((*C.struct_lyd_node)(yp.root)) + yp.root = nil + } + + return YParserError {ErrCode : YP_SUCCESS,} +} + +//Set operation +func (yp *YParser) SetOperation(op string) YParserError { + if (ypOpNode == nil) { + return YParserError {ErrCode : YP_INTERNAL_UNKNOWN,} + } + + if (0 != C.lyd_change_leaf_data((*C.struct_lyd_node)(ypOpNode), C.CString(op))) { + return YParserError {ErrCode : YP_INTERNAL_UNKNOWN,} + } + + yp.operation = op + return YParserError {ErrCode : YP_SUCCESS,} +} + +//Validate config - syntax and semantics +func (yp *YParser) ValidateData(data, depData *YParserNode) YParserError { + + var dataRoot *YParserNode + + if (depData != nil) { + if dataRoot, _ = yp.MergeSubtree(data, depData); dataRoot == nil { + CVL_LOG(ERROR, "Failed to merge dependent data\n") + return getErrorDetails() + } + } + + dataRootTmp := (*C.struct_lyd_node)(dataRoot) + + if (0 != C.lyd_data_validate(&dataRootTmp, C.LYD_OPT_CONFIG, (*C.struct_ly_ctx)(ypCtx))) { + if (Tracing == true) { + strData := yp.NodeDump((*YParserNode)(dataRootTmp)) + TRACE_LOG(TRACE_ONERROR, "Failed to validate data = %v", strData) + } + + CVL_LOG(ERROR, "Validation failed\n") + return getErrorDetails() + } + + return YParserError {ErrCode : YP_SUCCESS,} +} + +//Perform syntax checks +func (yp *YParser) ValidateSyntax(data *YParserNode) YParserError { + dataTmp := (*C.struct_lyd_node)(data) + + //Just validate syntax + if (0 != C.lyd_data_validate(&dataTmp, C.LYD_OPT_EDIT | C.LYD_OPT_NOEXTDEPS, + (*C.struct_ly_ctx)(ypCtx))) { + if (Tracing == true) { + strData := yp.NodeDump((*YParserNode)(dataTmp)) + TRACE_LOG(TRACE_ONERROR, "Failed to validate Syntax, data = %v", strData) + } + return getErrorDetails() + } + //fmt.Printf("Error Code from libyang is %d\n", C.ly_errno) + + return YParserError {ErrCode : YP_SUCCESS,} +} + +//Perform semantic checks +func (yp *YParser) ValidateSemantics(data, depData, appDepData *YParserNode) YParserError { + + var dataTmp *C.struct_lyd_node + + if (data != nil) { + dataTmp = (*C.struct_lyd_node)(data) + } else if (depData != nil) { + dataTmp = (*C.struct_lyd_node)(depData) + } else if (yp.root != nil) { + dataTmp = (*C.struct_lyd_node)(yp.root) + } else { + if (yp.operation == "CREATE") || (yp.operation == "UPDATE") { + return YParserError {ErrCode : YP_INTERNAL_UNKNOWN,} + } else { + return YParserError {ErrCode : YP_SUCCESS,} + } + } + + //parse dependent data + if (data != nil && depData != nil) { + + //merge input data and dependent data for semantic validation + if (0 != C.lyd_merge_to_ctx(&dataTmp, (*C.struct_lyd_node)(depData), + C.LYD_OPT_DESTRUCT, (*C.struct_ly_ctx)(ypCtx))) { + TRACE_LOG((TRACE_SEMANTIC | TRACE_LIBYANG), "Unable to merge dependent data\n") + return getErrorDetails() + } + } + + //Merge cached data + if ((data != nil || depData != nil) && yp.root != nil) { + if (0 != C.lyd_merge_to_ctx(&dataTmp, (*C.struct_lyd_node)(yp.root), + 0, (*C.struct_ly_ctx)(ypCtx))) { + TRACE_LOG((TRACE_SEMANTIC | TRACE_LIBYANG), "Unable to merge cached dependent data\n") + return getErrorDetails() + } + } + + //Merge appDepData + if (appDepData != nil) { + if (0 != C.lyd_merge_to_ctx(&dataTmp, (*C.struct_lyd_node)(appDepData), + C.LYD_OPT_DESTRUCT, (*C.struct_ly_ctx)(ypCtx))) { + TRACE_LOG((TRACE_SEMANTIC | TRACE_LIBYANG), "Unable to merge other dependent data\n") + return getErrorDetails() + } + } + + //Add operation for constraint check + if (ypOpRoot != nil) { + //if (0 != C.lyd_insert_sibling(&dataTmp, (*C.struct_lyd_node)(ypOpRoot))) { + if (0 != C.lyd_merge_to_ctx(&dataTmp, (*C.struct_lyd_node)(ypOpRoot), + 0, (*C.struct_ly_ctx)(ypCtx))) { + TRACE_LOG((TRACE_SEMANTIC | TRACE_LIBYANG), "Unable to insert operation node") + return getErrorDetails() + } + } + + if (Tracing == true) { + strData := yp.NodeDump((*YParserNode)(dataTmp)) + TRACE_LOG(TRACE_YPARSER, "Semantics data = %v", strData) + } + + //Check semantic validation + if (0 != C.lyd_data_validate(&dataTmp, C.LYD_OPT_CONFIG, (*C.struct_ly_ctx)(ypCtx))) { + if (Tracing == true) { + strData1 := yp.NodeDump((*YParserNode)(dataTmp)) + TRACE_LOG(TRACE_ONERROR, "Failed to validate Semantics, data = %v", strData1) + } + return getErrorDetails() + } + + return YParserError {ErrCode : YP_SUCCESS,} +} + +func (yp *YParser) FreeNode(node *YParserNode) YParserError { + + C.lyd_free_withsiblings((*C.struct_lyd_node)(node)) + + return YParserError {ErrCode : YP_SUCCESS,} +} + +/* This function translates LIBYANG error code to valid YPARSER error code. */ +func translateLYErrToYParserErr(LYErrcode int) YParserRetCode { + var ypErrCode YParserRetCode + + switch LYErrcode { + case C.LYVE_SUCCESS: /**< no error */ + ypErrCode = YP_SUCCESS + case C.LYVE_XML_MISS, C.LYVE_INARG, C.LYVE_MISSELEM: /**< missing XML object */ + ypErrCode = YP_SYNTAX_MISSING_FIELD + case C.LYVE_XML_INVAL, C.LYVE_XML_INCHAR, C.LYVE_INMOD, C.LYVE_INELEM , C.LYVE_INVAL, C.LYVE_MCASEDATA:/**< invalid XML object */ + ypErrCode = YP_SYNTAX_INVALID_FIELD + case C.LYVE_EOF, C.LYVE_INSTMT, C.LYVE_INPAR, C.LYVE_INID, C.LYVE_MISSSTMT, C.LYVE_MISSARG: /**< invalid statement (schema) */ + ypErrCode = YP_SYNTAX_INVALID_INPUT_DATA + case C.LYVE_TOOMANY: /**< too many instances of some object */ + ypErrCode = YP_SYNTAX_MULTIPLE_INSTANCE + case C.LYVE_DUPID, C.LYVE_DUPLEAFLIST, C.LYVE_DUPLIST, C.LYVE_NOUNIQ:/**< duplicated identifier (schema) */ + ypErrCode = YP_SYNTAX_DUPLICATE + case C.LYVE_ENUM_INVAL: /**< invalid enum value (schema) */ + ypErrCode = YP_SYNTAX_ENUM_INVALID + case C.LYVE_ENUM_INNAME: /**< invalid enum name (schema) */ + ypErrCode = YP_SYNTAX_ENUM_INVALID_NAME + case C.LYVE_ENUM_WS: /**< enum name with leading/trailing whitespaces (schema) */ + ypErrCode = YP_SYNTAX_ENUM_WHITESPACE + case C.LYVE_KEY_NLEAF, C.LYVE_KEY_CONFIG, C.LYVE_KEY_TYPE : /**< list key is not a leaf (schema) */ + ypErrCode = YP_SEMANTIC_KEY_INVALID + case C.LYVE_KEY_MISS, C.LYVE_PATH_MISSKEY: /**< list key not found (schema) */ + ypErrCode = YP_SEMANTIC_KEY_NOT_EXIST + case C.LYVE_KEY_DUP: /**< duplicated key identifier (schema) */ + ypErrCode = YP_SEMANTIC_KEY_DUPLICATE + case C.LYVE_NOMIN:/**< min-elements constraint not honored (data) */ + ypErrCode = YP_SYNTAX_MINIMUM_INVALID + case C.LYVE_NOMAX:/**< max-elements constraint not honored (data) */ + ypErrCode = YP_SYNTAX_MAXIMUM_INVALID + case C.LYVE_NOMUST, C.LYVE_NOWHEN, C.LYVE_INWHEN, C.LYVE_NOLEAFREF : /**< unsatisfied must condition (data) */ + ypErrCode = YP_SEMANTIC_DEPENDENT_DATA_MISSING + case C.LYVE_NOMANDCHOICE:/**< max-elements constraint not honored (data) */ + ypErrCode = YP_SEMANTIC_MANDATORY_DATA_MISSING + case C.LYVE_PATH_EXISTS: /**< target node already exists (path) */ + ypErrCode = YP_SEMANTIC_KEY_ALREADY_EXIST + default: + ypErrCode = YP_INTERNAL_UNKNOWN + + } + return ypErrCode +} + +/* This function performs parsing and processing of LIBYANG error messages. */ +func getErrorDetails() YParserError { + var key []string + var errtableName string + var ElemVal string + var errMessage string + var ElemName string + var errText string + var msg string + var ypErrCode YParserRetCode = YP_INTERNAL_UNKNOWN + var errMsg, errPath, errAppTag string + + ctx := (*C.struct_ly_ctx)(ypCtx) + ypErrFirst := C.ly_err_first(ctx); + + + if (ypErrFirst == nil) { + return YParserError { + TableName : errtableName, + ErrCode : ypErrCode, + Keys : key, + Value : ElemVal, + Field : ElemName, + Msg : errMessage, + ErrTxt: errText, + ErrAppTag: errAppTag, + } + } + + + if ((ypErrFirst != nil) && ypErrFirst.prev.no == C.LY_SUCCESS) { + return YParserError { + ErrCode : YP_SUCCESS, + } + } + + if (ypErrFirst != nil) { + errMsg = C.GoString(ypErrFirst.prev.msg) + errPath = C.GoString(ypErrFirst.prev.path) + errAppTag = C.GoString(ypErrFirst.prev.apptag) + } + + + /* Example error messages. + 1. Leafref "/sonic-port:sonic-port/sonic-port:PORT/sonic-port:ifname" of value "Ethernet668" points to a non-existing leaf. + (path: /sonic-interface:sonic-interface/INTERFACE[portname='Ethernet668'][ip_prefix='10.0.0.0/31']/portname) + 2. A vlan interface member cannot be part of portchannel which is already a vlan member + (path: /sonic-vlan:sonic-vlan/VLAN[name='Vlan1001']/members[.='Ethernet8']) + 3. Value "ch1" does not satisfy the constraint "Ethernet([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[0-9])" (range, length, or pattern). + (path: /sonic-vlan:sonic-vlan/VLAN[name='Vlan1001']/members[.='ch1'])*/ + + + /* Fetch the TABLE Name which are in CAPS. */ + resultTable := strings.SplitN(errPath, "[", 2) + if (len(resultTable) >= 2) { + resultTab := strings.Split(resultTable[0], "/") + errtableName = resultTab[len(resultTab) -1] + + /* Fetch the Error Elem Name. */ + resultElem := strings.Split(resultTable[1], "/") + ElemName = resultElem[len(resultElem) -1] + } + + /* Fetch the invalid field name. */ + result := strings.Split(errMsg, "\"") + if (len(result) > 1) { + for i := range result { + if (strings.Contains(result[i], "value")) || + (strings.Contains(result[i], "Value")) { + ElemVal = result[i+1] + } + } + } else if (len(result) == 1) { + /* Custom contraint error message like in must statement. + This can be used by App to display to user. + */ + errText = errMsg + } + + // Find key elements + resultKey := strings.Split(errPath, "=") + for i := range resultKey { + if (strings.Contains(resultKey[i], "]")) { + newRes := strings.Split(resultKey[i], "]") + key = append(key, newRes[0]) + } + } + + /* Form the error message. */ + msg = "[" + for _, elem := range key { + msg = msg + elem + " " + } + msg = msg + "]" + + /* For non-constraint related errors , print below error message. */ + if (len(result) > 1) { + errMessage = errtableName + " with keys" + msg + " has field " + + ElemName + " with invalid value " + ElemVal + }else { + /* Dependent data validation error. */ + errMessage = "Dependent data validation failed for table " + + errtableName + " with keys" + msg + } + + + if (C.ly_errno == C.LY_EVALID) { //Validation failure + ypErrCode = translateLYErrToYParserErr(int(ypErrFirst.prev.vecode)) + + } else { + switch (C.ly_errno) { + case C.LY_EMEM: + errText = "Memory allocation failure" + + case C.LY_ESYS: + errText = "System call failure" + + case C.LY_EINVAL: + errText = "Invalid value" + + case C.LY_EINT: + errText = "Internal error" + + case C.LY_EPLUGIN: + errText = "Error reported by a plugin" + } + } + + errObj := YParserError { + TableName : errtableName, + ErrCode : ypErrCode, + Keys : key, + Value : ElemVal, + Field : ElemName, + Msg : errMessage, + ErrTxt: errText, + ErrAppTag: errAppTag, + } + + TRACE_LOG(TRACE_YPARSER, "YParser error details: %v...", errObj) + + return errObj +} + +func FindNode(root *YParserNode, xpath string) *YParserNode { + return (*YParserNode)(C.lyd_find_node((*C.struct_lyd_node)(root), C.CString(xpath))) +} + +func GetModelNs(module *YParserModule) (ns, prefix string) { + return C.GoString(((*C.struct_lys_module)(module)).ns), + C.GoString(((*C.struct_lys_module)(module)).prefix) +} + +//Get model info for YANG list and its subtree +func GetModelListInfo(module *YParserModule) []*YParserListInfo { + var list []*YParserListInfo + + mod := (*C.struct_lys_module)(module) + set := C.lys_find_path(mod, nil, + C.CString(fmt.Sprintf("/%s/*", C.GoString(mod.name)))) + + if (set == nil) { + return nil + } + + for idx := 0; idx < int(set.number); idx++ { //for each container + + snode := C.lys_get_snode(set, C.int(idx)) + snodec := (*C.struct_lys_node_container)(unsafe.Pointer(snode)) + slist := (*C.struct_lys_node_list)(unsafe.Pointer(snodec.child)) + + //for each list + for ; slist != nil; slist = (*C.struct_lys_node_list)(unsafe.Pointer(slist.next)) { + var l YParserListInfo + listName := C.GoString(slist.name) + l.RedisTableName = C.GoString(snodec.name) + + tableName := listName + if (strings.HasSuffix(tableName, "_LIST")) { + tableName = tableName[0:len(tableName) - len("_LIST")] + } + l.ListName = tableName + l.ModelName = C.GoString(mod.name) + //Default database is CONFIG_DB since CVL works with config db mainly + l.Module = module + l.DbName = "CONFIG_DB" + //default delim '|' + l.RedisKeyDelim = "|" + //Default table size is -1 i.e. size limit + l.RedisTableSize = -1 + if (slist.max > 0) { + l.RedisTableSize = int(slist.max) + } + + l.LeafRef = make(map[string][]string) + l.XpathExpr = make(map[string]*XpathExpression) + l.CustValidation = make(map[string]string) + + //Add keys + keys := (*[10]*C.struct_lys_node_leaf)(unsafe.Pointer(slist.keys)) + for idx := 0; idx < int(slist.keys_size); idx++ { + keyName := C.GoString(keys[idx].name) + l.Keys = append(l.Keys, keyName) + } + + //Check for must expression + if (slist.must_size > 0) { + l.XpathExpr[listName] = &XpathExpression{C.GoString(slist.must.expr), + C.GoString(slist.must.eapptag), C.GoString(slist.must.emsg)} + } + + //Check for custom extension + if (slist.ext_size > 0) { + exts := (*[10]*C.struct_lys_ext_instance)(unsafe.Pointer(slist.ext)) + for idx := 0; idx < int(slist.ext_size); idx++ { + + extName := C.GoString(exts[idx].def.name) + argVal := C.GoString(exts[idx].arg_value) + + switch extName { + case "custom-validation": + if (argVal != "") { + l.CustValidation[listName] = argVal + } + case "db-name": + l.DbName = argVal + case "key-delim": + l.RedisKeyDelim = argVal + case "key-pattern": + l.RedisKeyPattern = argVal + case "map-leaf": + l.MapLeaf = strings.Split(argVal, " ") + } + } + + } + + //Add default key pattern + if l.RedisKeyPattern == "" { + keyPattern := []string{tableName} + for idx := 0; idx < len(l.Keys); idx++ { + keyPattern = append(keyPattern, fmt.Sprintf("{%s}", l.Keys[idx])) + } + l.RedisKeyPattern = strings.Join(keyPattern, l.RedisKeyDelim) + } + + + + for sChild := slist.child; sChild != nil; sChild = sChild.next { + sleaf := (*C.struct_lys_node_leaf)(unsafe.Pointer(sChild)) + if sleaf == nil { + continue + } + + leafName := C.GoString(sleaf.name) + + //Check for leafref expression + leafRefs := C.lys_get_leafrefs(sleaf) + if (leafRefs != nil) { + leafRefPaths := (*[10]*C.char)(unsafe.Pointer(&leafRefs.path)) + for idx := 0; idx < int(leafRefs.count); idx++ { + l.LeafRef[leafName] = append(l.LeafRef[leafName], + C.GoString(leafRefPaths[idx])) + } + } + + //Check for must expression + if (sleaf.must_size > 0) { + l.XpathExpr[leafName] = &XpathExpression{C.GoString(sleaf.must.expr), + C.GoString(sleaf.must.eapptag), C.GoString(sleaf.must.emsg)} + } + + //Check for custom extension + if (sleaf.ext_size > 0) { + exts := (*[10]*C.struct_lys_ext_instance)(unsafe.Pointer(sleaf.ext)) + for idx := 0; idx < int(sleaf.ext_size); idx++ { + if (C.GoString(exts[idx].def.name) == "custom-validation") { + argVal := C.GoString(exts[idx].arg_value) + if (argVal != "") { + l.CustValidation[leafName] = argVal + } + } + } + } + } + + list = append(list, &l) + }//each list inside a container + }//each container + + C.free(unsafe.Pointer(set)) + return list +} + diff --git a/src/cvl/jsondata_test.go b/src/cvl/jsondata_test.go new file mode 100644 index 0000000000..592ef71268 --- /dev/null +++ b/src/cvl/jsondata_test.go @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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{`{ + "stage": "INGRESS", + "type": "L3" + }`} + +var json_edit_config_create_acl_rule_config_data = []string{ + `{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000" + + + }`} + +var json_validate_config_data = []string{`{ + "INTERFACE": { + "Ethernet8|10.0.0.0/31": {}, + "Ethernet12|10.0.0.2/31": {}, + "Ethernet16|10.0.0.4/31": {} + } + }`, + `{ + "DEVICE_METADATA": { + "localhost": { + "hwsku": "Force10-S6100", + "default_bgp_status": "up", + "docker_routing_config_mode": "unified", + "hostname": "sonic-s6100-01", + "platform": "x86_64-dell_s6100_c2538-r0", + "mac": "4c:76:25:f4:70:82", + "default_pfcwd_status": "disable", + "deployment_id": "1", + "type": "ToRRouter" + } + } + }`, + `{ + "CABLE_LENGTH": { + "AZURE": { + "Ethernet8": "5m", + "Ethernet12": "5m", + "Ethernet16": "5m", + } + } + }`} diff --git a/src/cvl/schema/Makefile b/src/cvl/schema/Makefile new file mode 100644 index 0000000000..838f038499 --- /dev/null +++ b/src/cvl/schema/Makefile @@ -0,0 +1,76 @@ +################################################################################ +# # +# Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or # +# its subsidiaries. # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +# # +################################################################################ + +sonic_yang=../../../models/yang/sonic +sonic_yang_common=../../../models/yang/sonic/common +sonic_yang_platform=../../../models/yang/sonic/platform +pyang_plugin_dir=../../../tools/pyang/pyang_plugins + +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_platform=$(patsubst %.yang, %.yin, $(shell find $(sonic_yang_platform) -name '*.yang' | cut -d'/' -f7-9)) +out_platform_dep=$(shell find $(sonic_yang_platform) -name '*.yang') +out_tree=$(patsubst %.yang, %.tree, $(src_files)) +search_path=$(sonic_yang):$(sonic_yang_common):$(sonic_yang_common)/ietf + + +all: schema + +schema: $(out) $(out_common) $(out_platform) + @$(call install_cvl_schema) + +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 $(search_path) --plugindir $(pyang_plugin_dir) \ + -f yin-cvl $$devOpt $< -o `basename $@` + +#Build platform specific YANG models +%.yin:$(out_platform_dep) + @mkdir -p `dirname $@` + @echo "Generating $@ ..." + @pyang -p $(search_path) --plugindir $(pyang_plugin_dir) \ + -f yin-cvl $(sonic_yang)/`echo $@ | sed 's/yin/yang/g'` -o $@ + +%.tree:%.yang + @echo "Generating `basename $@` ..." + @devFile="`echo $< | cut -d . -f1`-dev.yang"; \ + if [ -f $$devFile ] ; then devOpt="--deviation-module $$devFile"; fi; \ + pyang -p $(search_path) -f tree $$devOpt $< -o `basename $@` + +clean: + @echo "Removing files ..." + rm -rf *.yin *.tree + rm -rf platform/ + 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/testdata/acl_rule.json b/src/cvl/testdata/acl_rule.json new file mode 100644 index 0000000000..01803b4f1d --- /dev/null +++ b/src/cvl/testdata/acl_rule.json @@ -0,0 +1,9 @@ +{ +"ACL_RULE": { + "TestACL13|Rule1": { + "PRIORITY": "55", + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "0" + } + } +} diff --git a/src/cvl/testdata/aclrule.json b/src/cvl/testdata/aclrule.json new file mode 100644 index 0000000000..6b03a5104b --- /dev/null +++ b/src/cvl/testdata/aclrule.json @@ -0,0 +1,8 @@ +{ + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000" +} diff --git a/src/cvl/testdata/acltable.json b/src/cvl/testdata/acltable.json new file mode 100644 index 0000000000..810caee0db --- /dev/null +++ b/src/cvl/testdata/acltable.json @@ -0,0 +1,4 @@ +{ +"stage": "INGRESS", +"type": "L3" +} diff --git a/src/cvl/testdata/config_db.json b/src/cvl/testdata/config_db.json new file mode 100644 index 0000000000..ad2d952f28 --- /dev/null +++ b/src/cvl/testdata/config_db.json @@ -0,0 +1,107 @@ +{ + "VLAN": { + "Vlan100": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + }, + "Vlan1200": { + "members": [ + "Ethernet64", + "Ethernet8" + ], + "vlanid": "1200" + }, + "Vlan2500": { + "members": [ + "Ethernet8", + "Ethernet64" + ], + "vlanid": "2500" + } + }, + "VLAN_MEMBER": { + "Vlan100|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan100|Ethernet44": { + "tagging_mode": "tagged" + }, + "Vlan1200|Ethernet8": { + "tagging_mode": "tagged" + } + }, + "WRED_PROFILE": { + "AZURE_LOSSLESS": { + "red_max_threshold": "312000", + "wred_green_enable": "true", + "ecn": "ecn_all", + "green_min_threshold": "104000", + "red_min_threshold": "104000", + "wred_yellow_enable": "true", + "yellow_min_threshold": "104000", + "wred_red_enable": "true", + "yellow_max_threshold": "312000", + "green_max_threshold": "312000" + } + }, + "BUFFER_POOL": { + "egress_lossless_pool": { + "type": "egress", + "mode": "static", + "size": "12766208" + }, + "egress_lossy_pool": { + "type": "egress", + "mode": "dynamic", + "size": "8072396" + }, + "ingress_lossless_pool": { + "type": "ingress", + "mode": "dynamic", + "size": "12766208" + } + }, + "MIRROR_SESSION": { + "everflow0": { + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2" + } + }, + "SCHEDULER": { + "scheduler.0": { + "type": "DWRR", + "weight": "25" + }, + "scheduler.1": { + "type": "DWRR", + "weight": "30" + }, + "scheduler.2": { + "type": "DWRR", + "weight": "20" + } + }, + "QUEUE": { + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|0": { + "scheduler": "[SCHEDULER|scheduler.1]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|1": { + "scheduler": "[SCHEDULER|scheduler.2]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|3-4": { + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]", + "scheduler": "[SCHEDULER|scheduler.0]" + } + }, + "TC_TO_QUEUE_MAP": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "4": "4" + } + } +} diff --git a/src/cvl/testdata/config_db1.json b/src/cvl/testdata/config_db1.json new file mode 100644 index 0000000000..ec6ce5e734 --- /dev/null +++ b/src/cvl/testdata/config_db1.json @@ -0,0 +1,133 @@ +{ + "PORT": { + "Ethernet0": { + "alias": "fortyGigE0/0", + "lanes": "29,30,31,32" + }, + "Ethernet4": { + "alias": "fortyGigE0/4", + "lanes": "25,26,27,28" + }, + "Ethernet8": { + "alias": "fortyGigE0/8", + "lanes": "37,38,39,40" + }, + "Ethernet12": { + "alias": "fortyGigE0/12", + "lanes": "33,34,35,36" + }, + "Ethernet16": { + "alias": "fortyGigE0/16", + "lanes": "41,42,43,44" + }, + "Ethernet20": { + "alias": "fortyGigE0/20", + "lanes": "45,46,47,48" + }, + "Ethernet24": { + "alias": "fortyGigE0/24", + "lanes": "5,6,7,8" + }, + "Ethernet28": { + "alias": "fortyGigE0/28", + "lanes": "1,2,3,4" + }, + "Ethernet32": { + "alias": "fortyGigE0/32", + "lanes": "9,10,11,12" + }, + "Ethernet36": { + "alias": "fortyGigE0/36", + "lanes": "13,14,15,16" + }, + "Ethernet40": { + "alias": "fortyGigE0/40", + "lanes": "21,22,23,24" + }, + "Ethernet44": { + "alias": "fortyGigE0/44", + "lanes": "17,18,19,20" + }, + "Ethernet48": { + "alias": "fortyGigE0/48", + "lanes": "49,50,51,52" + }, + "Ethernet52": { + "alias": "fortyGigE0/52", + "lanes": "53,54,55,56" + }, + "Ethernet56": { + "alias": "fortyGigE0/56", + "lanes": "61,62,63,64" + }, + "Ethernet60": { + "alias": "fortyGigE0/60", + "lanes": "57,58,59,60" + }, + "Ethernet64": { + "alias": "fortyGigE0/64", + "lanes": "65,66,67,68" + }, + "Ethernet68": { + "alias": "fortyGigE0/68", + "lanes": "69,70,71,72" + }, + "Ethernet72": { + "alias": "fortyGigE0/72", + "lanes": "77,78,79,80" + }, + "Ethernet76": { + "alias": "fortyGigE0/76", + "lanes": "73,74,75,76" + }, + "Ethernet80": { + "alias": "fortyGigE0/80", + "lanes": "105,106,107,108" + }, + "Ethernet84": { + "alias": "fortyGigE0/84", + "lanes": "109,110,111,112" + }, + "Ethernet88": { + "alias": "fortyGigE0/88", + "lanes": "117,118,119,120" + }, + "Ethernet92": { + "alias": "fortyGigE0/92", + "lanes": "113,114,115,116" + }, + "Ethernet96": { + "alias": "fortyGigE0/96", + "lanes": "121,122,123,124" + }, + "Ethernet100": { + "alias": "fortyGigE0/100", + "lanes": "125,126,127,128" + }, + "Ethernet104": { + "alias": "fortyGigE0/104", + "lanes": "85,86,87,88" + }, + "Ethernet108": { + "alias": "fortyGigE0/108", + "lanes": "81,82,83,84" + }, + "Ethernet112": { + "alias": "fortyGigE0/112", + "lanes": "89,90,91,92" + }, + "Ethernet116": { + "alias": "fortyGigE0/116", + "lanes": "93,94,95,96" + }, + "Ethernet120": { + "alias": "fortyGigE0/120", + "lanes": "97,98,99,100" + }, + "Ethernet124": { + "alias": "fortyGigE0/124", + "lanes": "101,102,103,104" + } + } +} + diff --git a/src/cvl/testdata/config_db2.json b/src/cvl/testdata/config_db2.json new file mode 100644 index 0000000000..e6be83ac4e --- /dev/null +++ b/src/cvl/testdata/config_db2.json @@ -0,0 +1,3437 @@ +{ + "VLAN_MEMBER": { + "Vlan51|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan51|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan97|Ethernet4": { + "tagging_mode": "untagged" + }, + "Vlan99|Ethernet4": { + "tagging_mode": "untagged" + }, + "Vlan99|Ethernet108": { + "tagging_mode": "untagged" + }, + "Vlan99|Ethernet124": { + "tagging_mode": "untagged" + }, + "Vlan101|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan101|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan101|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan102|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan102|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan102|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan103|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan103|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan103|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan104|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan104|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan104|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan105|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan105|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan105|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan106|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan106|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan106|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan107|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan107|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan107|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan108|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan108|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan108|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan109|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan109|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan109|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan110|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan110|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan110|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan111|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan111|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan111|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan112|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan112|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan112|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan113|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan113|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan113|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan114|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan114|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan114|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan115|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan115|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan115|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan116|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan116|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan116|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan117|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan117|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan117|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan118|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan118|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan118|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan119|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan119|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan119|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan120|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan120|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan120|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan121|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan121|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan121|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan122|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan122|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan122|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan123|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan123|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan123|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan124|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan124|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan124|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan125|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan125|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan125|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan126|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan126|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan126|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan127|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan127|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan127|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan128|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan128|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan128|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan129|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan129|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan129|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan130|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan130|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan130|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan131|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan131|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan131|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan132|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan132|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan132|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan133|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan133|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan133|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan134|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan134|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan134|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan135|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan135|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan135|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan136|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan136|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan136|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan137|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan137|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan137|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan138|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan138|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan138|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan139|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan139|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan139|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan140|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan140|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan140|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan141|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan141|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan141|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan142|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan142|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan142|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan143|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan143|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan143|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan144|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan144|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan144|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan145|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan145|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan145|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan146|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan146|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan146|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan147|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan147|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan147|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan148|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan148|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan148|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan149|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan149|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan149|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan150|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan150|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan150|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan151|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan151|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan151|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan152|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan152|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan152|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan153|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan153|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan153|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan154|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan154|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan154|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan155|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan155|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan155|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan156|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan156|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan156|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan157|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan157|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan157|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan158|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan158|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan158|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan159|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan159|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan159|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan160|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan160|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan160|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan161|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan161|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan161|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan162|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan162|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan162|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan163|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan163|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan163|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan164|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan164|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan164|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan165|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan165|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan165|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan166|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan166|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan166|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan167|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan167|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan167|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan168|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan168|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan168|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan169|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan169|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan169|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan170|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan170|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan170|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan171|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan171|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan171|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan172|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan172|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan172|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan173|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan173|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan173|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan174|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan174|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan174|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan175|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan175|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan175|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan176|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan176|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan176|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan177|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan177|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan177|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan178|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan178|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan178|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan179|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan179|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan179|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan180|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan180|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan180|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan181|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan181|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan181|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan182|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan182|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan182|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan183|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan183|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan183|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan184|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan184|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan184|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan185|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan185|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan185|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan186|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan186|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan186|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan187|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan187|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan187|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan188|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan188|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan188|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan189|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan189|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan189|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan190|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan190|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan190|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan191|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan191|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan191|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan192|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan192|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan192|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan193|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan193|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan193|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan194|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan194|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan194|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan195|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan195|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan195|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan196|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan196|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan196|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan197|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan197|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan197|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan198|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan198|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan198|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan199|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan200|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan200|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan200|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan201|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan201|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan201|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan202|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan202|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan202|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan203|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan203|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan203|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan204|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan204|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan204|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan205|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan205|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan205|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan206|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan206|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan206|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan207|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan207|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan207|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan208|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan208|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan208|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan209|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan209|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan209|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan210|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan210|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan210|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan211|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan211|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan211|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan212|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan212|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan212|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan213|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan213|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan213|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan214|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan214|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan214|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan215|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan215|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan215|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan216|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan216|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan216|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan217|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan217|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan217|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan218|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan218|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan218|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan219|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan219|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan219|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan220|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan220|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan220|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan221|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan221|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan221|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan222|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan222|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan222|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan223|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan223|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan223|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan224|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan224|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan224|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan225|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan225|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan225|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan226|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan226|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan226|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan227|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan227|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan227|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan228|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan228|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan228|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan229|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan229|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan229|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan230|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan230|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan230|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan231|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan231|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan231|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan232|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan232|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan232|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan233|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan233|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan233|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan234|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan234|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan234|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan235|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan235|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan235|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan236|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan236|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan236|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan237|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan237|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan237|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan238|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan238|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan238|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan239|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan239|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan239|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan240|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan240|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan240|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan241|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan241|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan241|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan242|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan242|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan242|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan243|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan243|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan243|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan244|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan244|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan244|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan245|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan245|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan245|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan246|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan246|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan246|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan247|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan247|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan247|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan248|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan248|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan248|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan249|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan249|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan249|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan250|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan250|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan250|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan251|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan251|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan251|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan252|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan252|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan252|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan253|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan253|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan253|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan254|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan254|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan254|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan255|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan255|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan255|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan256|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan256|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan256|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan257|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan257|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan257|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan258|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan258|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan258|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan259|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan259|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan259|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan260|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan260|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan260|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan261|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan261|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan261|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan262|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan262|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan262|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan263|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan263|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan263|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan264|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan264|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan264|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan265|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan265|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan265|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan266|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan266|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan266|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan267|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan267|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan267|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan268|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan268|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan268|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan269|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan269|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan269|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan270|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan270|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan270|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan271|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan271|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan271|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan272|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan272|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan272|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan273|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan273|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan273|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan274|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan274|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan274|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan275|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan275|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan275|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan276|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan276|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan276|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan277|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan277|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan277|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan278|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan278|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan278|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan279|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan279|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan279|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan280|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan280|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan280|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan281|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan281|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan281|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan282|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan282|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan282|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan283|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan283|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan283|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan284|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan284|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan284|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan285|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan285|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan285|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan286|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan286|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan286|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan287|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan287|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan287|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan288|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan288|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan288|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan289|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan289|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan289|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan290|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan290|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan290|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan291|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan291|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan291|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan292|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan292|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan292|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan293|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan293|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan293|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan294|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan294|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan294|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan295|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan295|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan295|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan296|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan296|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan296|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan297|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan297|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan297|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan298|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan298|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan298|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan299|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan299|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan299|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan300|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan300|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan300|Ethernet4": { + "tagging_mode": "tagged" + } + }, + "VLAN": { + "Vlan51": { + "members": [ + "Ethernet108", + "Ethernet124" + ], + "vlanid": "51" + }, + "Vlan97": { + "members": [ + "Ethernet4" + ], + "vlanid": "97" + }, + "Vlan99": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "99" + }, + "Vlan101": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "101" + }, + "Vlan102": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "102" + }, + "Vlan103": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "103" + }, + "Vlan104": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "104" + }, + "Vlan105": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "105" + }, + "Vlan106": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "106" + }, + "Vlan107": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "107" + }, + "Vlan108": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "108" + }, + "Vlan109": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "109" + }, + "Vlan110": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "110" + }, + "Vlan111": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "111" + }, + "Vlan112": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "112" + }, + "Vlan113": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "113" + }, + "Vlan114": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "114" + }, + "Vlan115": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "115" + }, + "Vlan116": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "116" + }, + "Vlan117": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "117" + }, + "Vlan118": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "118" + }, + "Vlan119": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "119" + }, + "Vlan120": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "120" + }, + "Vlan121": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "121" + }, + "Vlan122": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "122" + }, + "Vlan123": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "123" + }, + "Vlan124": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "124" + }, + "Vlan125": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "125" + }, + "Vlan126": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "126" + }, + "Vlan127": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "127" + }, + "Vlan128": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "128" + }, + "Vlan129": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "129" + }, + "Vlan130": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "130" + }, + "Vlan131": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "131" + }, + "Vlan132": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "132" + }, + "Vlan133": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "133" + }, + "Vlan134": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "134" + }, + "Vlan135": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "135" + }, + "Vlan136": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "136" + }, + "Vlan137": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "137" + }, + "Vlan138": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "138" + }, + "Vlan139": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "139" + }, + "Vlan140": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "140" + }, + "Vlan141": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "141" + }, + "Vlan142": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "142" + }, + "Vlan143": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "143" + }, + "Vlan144": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "144" + }, + "Vlan145": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "145" + }, + "Vlan146": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "146" + }, + "Vlan147": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "147" + }, + "Vlan148": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "148" + }, + "Vlan149": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "149" + }, + "Vlan150": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "150" + }, + "Vlan151": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "151" + }, + "Vlan152": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "152" + }, + "Vlan153": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "153" + }, + "Vlan154": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "154" + }, + "Vlan155": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "155" + }, + "Vlan156": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "156" + }, + "Vlan157": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "157" + }, + "Vlan158": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "158" + }, + "Vlan159": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "159" + }, + "Vlan160": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "160" + }, + "Vlan161": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "161" + }, + "Vlan162": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "162" + }, + "Vlan163": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "163" + }, + "Vlan164": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "164" + }, + "Vlan165": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "165" + }, + "Vlan166": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "166" + }, + "Vlan167": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "167" + }, + "Vlan168": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "168" + }, + "Vlan169": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "169" + }, + "Vlan170": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "170" + }, + "Vlan171": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "171" + }, + "Vlan172": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "172" + }, + "Vlan173": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "173" + }, + "Vlan174": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "174" + }, + "Vlan175": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "175" + }, + "Vlan176": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "176" + }, + "Vlan177": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "177" + }, + "Vlan178": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "178" + }, + "Vlan179": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "179" + }, + "Vlan180": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "180" + }, + "Vlan181": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "181" + }, + "Vlan182": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "182" + }, + "Vlan183": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "183" + }, + "Vlan184": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "184" + }, + "Vlan185": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "185" + }, + "Vlan186": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "186" + }, + "Vlan187": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "187" + }, + "Vlan188": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "188" + }, + "Vlan189": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "189" + }, + "Vlan190": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "190" + }, + "Vlan191": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "191" + }, + "Vlan192": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "192" + }, + "Vlan193": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "193" + }, + "Vlan194": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "194" + }, + "Vlan195": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "195" + }, + "Vlan196": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "196" + }, + "Vlan197": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "197" + }, + "Vlan198": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "198" + }, + "Vlan199": { + "members": [ + "Ethernet64" + ], + "vlanid": "199" + }, + "Vlan200": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "200" + }, + "Vlan201": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "201" + }, + "Vlan202": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "202" + }, + "Vlan203": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "203" + }, + "Vlan204": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "204" + }, + "Vlan205": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "205" + }, + "Vlan206": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "206" + }, + "Vlan207": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "207" + }, + "Vlan208": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "208" + }, + "Vlan209": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "209" + }, + "Vlan210": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "210" + }, + "Vlan211": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "211" + }, + "Vlan212": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "212" + }, + "Vlan213": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "213" + }, + "Vlan214": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "214" + }, + "Vlan215": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "215" + }, + "Vlan216": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "216" + }, + "Vlan217": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "217" + }, + "Vlan218": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "218" + }, + "Vlan219": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "219" + }, + "Vlan220": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "220" + }, + "Vlan221": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "221" + }, + "Vlan222": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "222" + }, + "Vlan223": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "223" + }, + "Vlan224": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "224" + }, + "Vlan225": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "225" + }, + "Vlan226": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "226" + }, + "Vlan227": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "227" + }, + "Vlan228": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "228" + }, + "Vlan229": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "229" + }, + "Vlan230": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "230" + }, + "Vlan231": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "231" + }, + "Vlan232": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "232" + }, + "Vlan233": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "233" + }, + "Vlan234": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "234" + }, + "Vlan235": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "235" + }, + "Vlan236": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "236" + }, + "Vlan237": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "237" + }, + "Vlan238": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "238" + }, + "Vlan239": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "239" + }, + "Vlan240": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "240" + }, + "Vlan241": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "241" + }, + "Vlan242": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "242" + }, + "Vlan243": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "243" + }, + "Vlan244": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "244" + }, + "Vlan245": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "245" + }, + "Vlan246": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "246" + }, + "Vlan247": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "247" + }, + "Vlan248": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "248" + }, + "Vlan249": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "249" + }, + "Vlan250": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "250" + }, + "Vlan251": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "251" + }, + "Vlan252": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "252" + }, + "Vlan253": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "253" + }, + "Vlan254": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "254" + }, + "Vlan255": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "255" + }, + "Vlan256": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "256" + }, + "Vlan257": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "257" + }, + "Vlan258": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "258" + }, + "Vlan259": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "259" + }, + "Vlan260": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "260" + }, + "Vlan261": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "261" + }, + "Vlan262": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "262" + }, + "Vlan263": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "263" + }, + "Vlan264": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "264" + }, + "Vlan265": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "265" + }, + "Vlan266": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "266" + }, + "Vlan267": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "267" + }, + "Vlan268": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "268" + }, + "Vlan269": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "269" + }, + "Vlan270": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "270" + }, + "Vlan271": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "271" + }, + "Vlan272": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "272" + }, + "Vlan273": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "273" + }, + "Vlan274": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "274" + }, + "Vlan275": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "275" + }, + "Vlan276": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "276" + }, + "Vlan277": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "277" + }, + "Vlan278": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "278" + }, + "Vlan279": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "279" + }, + "Vlan280": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "280" + }, + "Vlan281": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "281" + }, + "Vlan282": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "282" + }, + "Vlan283": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "283" + }, + "Vlan284": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "284" + }, + "Vlan285": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "285" + }, + "Vlan286": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "286" + }, + "Vlan287": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "287" + }, + "Vlan288": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "288" + }, + "Vlan289": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "289" + }, + "Vlan290": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "290" + }, + "Vlan291": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "291" + }, + "Vlan292": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "292" + }, + "Vlan293": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "293" + }, + "Vlan294": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "294" + }, + "Vlan295": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "295" + }, + "Vlan296": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "296" + }, + "Vlan297": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "297" + }, + "Vlan298": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "298" + }, + "Vlan299": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "299" + }, + "Vlan300": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "300" + } + } +} diff --git a/src/cvl/testdata/create_acl_table.json b/src/cvl/testdata/create_acl_table.json new file mode 100644 index 0000000000..604be2e2d2 --- /dev/null +++ b/src/cvl/testdata/create_acl_table.json @@ -0,0 +1,8 @@ +{ +"ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + } +} diff --git a/src/cvl/testdata/create_acl_table12.json b/src/cvl/testdata/create_acl_table12.json new file mode 100644 index 0000000000..83f74d6f18 --- /dev/null +++ b/src/cvl/testdata/create_acl_table12.json @@ -0,0 +1,8 @@ +{ +"ACL_TABLE": { + "TestACL13": { + "stage": "INGRESS", + "type": "L3" + } + } +} diff --git a/src/cvl/testdata/create_acl_table13.json b/src/cvl/testdata/create_acl_table13.json new file mode 100644 index 0000000000..83f74d6f18 --- /dev/null +++ b/src/cvl/testdata/create_acl_table13.json @@ -0,0 +1,8 @@ +{ +"ACL_TABLE": { + "TestACL13": { + "stage": "INGRESS", + "type": "L3" + } + } +} diff --git a/src/cvl/testdata/port_table.json b/src/cvl/testdata/port_table.json new file mode 100644 index 0000000000..ec6ce5e734 --- /dev/null +++ b/src/cvl/testdata/port_table.json @@ -0,0 +1,133 @@ +{ + "PORT": { + "Ethernet0": { + "alias": "fortyGigE0/0", + "lanes": "29,30,31,32" + }, + "Ethernet4": { + "alias": "fortyGigE0/4", + "lanes": "25,26,27,28" + }, + "Ethernet8": { + "alias": "fortyGigE0/8", + "lanes": "37,38,39,40" + }, + "Ethernet12": { + "alias": "fortyGigE0/12", + "lanes": "33,34,35,36" + }, + "Ethernet16": { + "alias": "fortyGigE0/16", + "lanes": "41,42,43,44" + }, + "Ethernet20": { + "alias": "fortyGigE0/20", + "lanes": "45,46,47,48" + }, + "Ethernet24": { + "alias": "fortyGigE0/24", + "lanes": "5,6,7,8" + }, + "Ethernet28": { + "alias": "fortyGigE0/28", + "lanes": "1,2,3,4" + }, + "Ethernet32": { + "alias": "fortyGigE0/32", + "lanes": "9,10,11,12" + }, + "Ethernet36": { + "alias": "fortyGigE0/36", + "lanes": "13,14,15,16" + }, + "Ethernet40": { + "alias": "fortyGigE0/40", + "lanes": "21,22,23,24" + }, + "Ethernet44": { + "alias": "fortyGigE0/44", + "lanes": "17,18,19,20" + }, + "Ethernet48": { + "alias": "fortyGigE0/48", + "lanes": "49,50,51,52" + }, + "Ethernet52": { + "alias": "fortyGigE0/52", + "lanes": "53,54,55,56" + }, + "Ethernet56": { + "alias": "fortyGigE0/56", + "lanes": "61,62,63,64" + }, + "Ethernet60": { + "alias": "fortyGigE0/60", + "lanes": "57,58,59,60" + }, + "Ethernet64": { + "alias": "fortyGigE0/64", + "lanes": "65,66,67,68" + }, + "Ethernet68": { + "alias": "fortyGigE0/68", + "lanes": "69,70,71,72" + }, + "Ethernet72": { + "alias": "fortyGigE0/72", + "lanes": "77,78,79,80" + }, + "Ethernet76": { + "alias": "fortyGigE0/76", + "lanes": "73,74,75,76" + }, + "Ethernet80": { + "alias": "fortyGigE0/80", + "lanes": "105,106,107,108" + }, + "Ethernet84": { + "alias": "fortyGigE0/84", + "lanes": "109,110,111,112" + }, + "Ethernet88": { + "alias": "fortyGigE0/88", + "lanes": "117,118,119,120" + }, + "Ethernet92": { + "alias": "fortyGigE0/92", + "lanes": "113,114,115,116" + }, + "Ethernet96": { + "alias": "fortyGigE0/96", + "lanes": "121,122,123,124" + }, + "Ethernet100": { + "alias": "fortyGigE0/100", + "lanes": "125,126,127,128" + }, + "Ethernet104": { + "alias": "fortyGigE0/104", + "lanes": "85,86,87,88" + }, + "Ethernet108": { + "alias": "fortyGigE0/108", + "lanes": "81,82,83,84" + }, + "Ethernet112": { + "alias": "fortyGigE0/112", + "lanes": "89,90,91,92" + }, + "Ethernet116": { + "alias": "fortyGigE0/116", + "lanes": "93,94,95,96" + }, + "Ethernet120": { + "alias": "fortyGigE0/120", + "lanes": "97,98,99,100" + }, + "Ethernet124": { + "alias": "fortyGigE0/124", + "lanes": "101,102,103,104" + } + } +} + diff --git a/src/cvl/testdata/schema/Makefile b/src/cvl/testdata/schema/Makefile new file mode 100644 index 0000000000..43aaf1aef9 --- /dev/null +++ b/src/cvl/testdata/schema/Makefile @@ -0,0 +1,49 @@ +################################################################################ +# # +# 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 + rm -rf platform 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..217e5d3739 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-device-metadata.yang @@ -0,0 +1,98 @@ +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"; + max-elements 1; + + 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/testdata/schema/sonic-pf-limits.yang b/src/cvl/testdata/schema/sonic-pf-limits.yang new file mode 100644 index 0000000000..658a6e9a8d --- /dev/null +++ b/src/cvl/testdata/schema/sonic-pf-limits.yang @@ -0,0 +1,44 @@ +module sonic-pf-limits { + namespace "http://github.com/Azure/sonic-pf-limits"; + prefix spf; + yang-version 1.1; + + import sonic-extension { + prefix sonic-ext; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC Platform constrainst"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-pf-limits { + sonic-ext:db-name "APPL_DB"; + + container acl { + leaf MAX_ACL_RULES { + type uint16; + } + leaf MAX_PRIORITY { + type uint16 { + range "1..65535"; + } + } + + } + container vlan { + leaf MAX_VLANS { + type uint16; + } + } + } +} 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/testdata/schema/sonic-port-qos-map.yang b/src/cvl/testdata/schema/sonic-port-qos-map.yang new file mode 100644 index 0000000000..f9785cb46a --- /dev/null +++ b/src/cvl/testdata/schema/sonic-port-qos-map.yang @@ -0,0 +1,87 @@ +module sonic-port-qos-map { + namespace "http://github.com/Azure/sonic-port-qos-map"; + prefix pqm; + + import sonic-extension { + prefix sonic-ext; + } + + import sonic-port { + prefix prt; + } + + import sonic-tc-priority-group-map { + prefix tpg; + } + + import sonic-tc-queue-map { + prefix tqm; + } + + import sonic-pfc-priority-queue-map { + prefix ppq; + } + + import sonic-dscp-tc-map { + prefix dtm; + } + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC PORT_QOS_MAP"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + container sonic-port-qos-map { + + list PORT_QOS_MAP { + key "ifname"; + 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: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: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:TC_TO_QUEUE_MAP_LIST/tqm:name"; + } + } + + leaf pfc-enable { + type string { + pattern "[0-9](,[0-9])?"; + } + } + + leaf pfc_to_queue_map { + type leafref { + 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: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..c9a9054267 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-portchannel-interface.yang @@ -0,0 +1,58 @@ +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"; + + leaf pch_name{ + type leafref { + path "/spc:sonic-portchannel/spc:PORTCHANNEL/spc:PORTCHANNEL_LIST/spc:name"; + } + } + + } + list PORTCHANNEL_INTERFACE_IPADDR_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..c70a80deea --- /dev/null +++ b/src/cvl/testdata/schema/sonic-portchannel.yang @@ -0,0 +1,136 @@ +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"; + + leaf name { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + 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"; + } + } + } + } + + container LAG_TABLE { + config false; + + list LAG_TABLE_LIST { + key "lagname"; + + leaf lagname { + type string; + } + + leaf admin_status { + type scommon:admin-status; + } + + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf active { + type boolean; + } + + leaf name { + type string; + } + + leaf oper_status { + type scommon:oper-status; + } + } + } + + container LAG_MEMBER_TABLE { + config false; + list LAG_MEMBER_TABLE_LIST { + key "name ifname"; + + leaf name { + type leafref { + path "../../../LAG_TABLE/LAG_TABLE_LIST/lagname"; + } + } + + 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-spanning-tree.yang b/src/cvl/testdata/schema/sonic-spanning-tree.yang new file mode 100755 index 0000000000..3bc96d53b6 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-spanning-tree.yang @@ -0,0 +1,251 @@ +module sonic-spanning-tree { + namespace "http://github.com/Azure/sonic-spanning-tree"; + prefix sstp; + yang-version 1.1; + + import sonic-common { + prefix scommon; + } + + import sonic-port { + prefix prt; + } + + import sonic-portchannel { + prefix spc; + } + + import sonic-vlan { + prefix svlan; + } + + organization + "BRCM"; + + contact + "BRCM"; + + description + "SONIC Spanning-tree"; + + revision 2019-09-21 { + description + "Initial revision."; + } + + grouping vlanModeAttr { + leaf forward_delay { + type uint8 { + range "4..30" { + error-message "Invalid Forwarding Delay value."; + } + } + units seconds; + default 15; + } + + leaf hello_time { + type uint8 { + range "1..10" { + error-message "Invalid Hello Time value."; + } + } + units seconds; + default 2; + } + + leaf max_age { + type uint8 { + range "6..40" { + error-message "Invalid Maximum Age Time value."; + } + } + units seconds; + default 20; + } + + leaf priority { + type uint16 { + range "0..61440" { + error-message "Invalid Bridge Priority value."; + } + } + units seconds; + default 20; + } + } + + grouping interfaceAttr { + leaf path_cost { + type uint64 { + range "1..200000000" { + error-message "Invalid Port Path Cost value."; + } + } + default 200; + } + + leaf priority { + type uint8 { + range "0..240" { + error-message "Invalid Port Priority value."; + } + } + default 128; + } + } + + container sonic-spanning-tree { + + container STP { + list STP_LIST { + max-elements 1; + key "keyleaf"; + + leaf keyleaf { + type enumeration { + enum GLOBAL; + } + } + + leaf mode { + type enumeration { + enum pvst; + enum rpvst; + enum mstp; + enum rstp; + } + } + + leaf rootguard_timeout { + type uint16 { + range "5..600" { + error-message "Invalid Root-guard Timeout value."; + } + } + units seconds; + default 30; + } + + leaf bpdu_filter { + type boolean; + } + + uses vlanModeAttr; + } + } + + container STP_VLAN { + list STP_VLAN_LIST { + key "name"; + + leaf name { + type leafref { + path "/svlan:sonic-vlan/svlan:VLAN/svlan:VLAN_LIST/svlan:name"; + } + } + + leaf vlanid { + type uint16 { + range "1..4095" { + error-message "Vlan ID out of range"; + error-app-tag vlanid-invalid; + } + } + } + + leaf enabled { + type boolean; + } + + uses vlanModeAttr; + } + } + + container STP_VLAN_INTF { + list STP_VLAN_INTF_LIST { + key "vlan-name ifname"; + + leaf vlan-name { + type leafref { + path "../../../STP_VLAN/STP_VLAN_LIST/name"; + } + } + + leaf ifname { + type leafref { + path "../../../STP_INTF/STP_INTF_LIST/ifname"; + } + } + + uses interfaceAttr; + } + } + + container STP_INTF { + list STP_INTF_LIST { + key "ifname"; + + leaf ifname { + 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"; + } + } + } + + leaf enabled { + type boolean; + } + + leaf root_guard { + type boolean; + } + + leaf bpdu_guard { + type boolean; + } + + leaf bpdu_filter { + type enumeration { + enum enable; + enum disable; + enum global; + } + } + + leaf bpdu_guard_do_disable { + type boolean; + } + + leaf uplink_fast { + type boolean; + } + + leaf portfast { + type boolean; + } + + uses interfaceAttr; + + // For RPVST+ + leaf edge_port { + //when ("../../../STP/STP_LIST/mode='rpvst'"); + type boolean; + } + + leaf link_type { + //when ("../../../STP/STP_LIST/mode='rpvst'"); + type enumeration { + enum auto; + enum shared; + enum point-to-point; + } + } + } + } + } +} 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/testdata/schema/sonic-vlan-deviation.yang b/src/cvl/testdata/schema/sonic-vlan-deviation.yang new file mode 100644 index 0000000000..ff426fb30c --- /dev/null +++ b/src/cvl/testdata/schema/sonic-vlan-deviation.yang @@ -0,0 +1,36 @@ +module sonic-vlan-deviation { + namespace "http://github.com/Azure/sonic-vlan-deviation"; + prefix svd; + yang-version 1.1; + + /* + import sonic-vlan { + prefix svlan; + } + */ + + organization + "SONiC"; + + contact + "SONiC"; + + description + "SONIC VLAN deviation file"; + + revision 2019-05-15 { + description + "Initial revision."; + } + + /* + deviation /svlan:sonic-vlan/svlan:VLAN/svlan:name { + deviate replace { + type string { + // Supports 3K VLANs + pattern "Vlan([1-3][0-9]{3}|[1-9][0-9]{2}|[1-9][0-9]|[1-9])"; + } + } + } + */ +} 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..7302307e14 --- /dev/null +++ b/src/cvl/testdata/schema/sonic-vlan.yang @@ -0,0 +1,119 @@ +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"; + } + } + } + leaf mtu { + type uint32 { + range "1312..9216" { + error-message "Invalid MTU value"; + error-app-tag mtu-invalid; + } + } + } + + leaf admin_status { + type scommon:admin-status; + } + } + } + + 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 new file mode 100644 index 0000000000..ce4dc21f0f --- /dev/null +++ b/src/cvl/tests/Makefile @@ -0,0 +1,37 @@ +################################################################################ +# # +# 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 ../../..) +GO=/usr/local/go/bin/go +GOPATH = $(TOPDIR):$(shell go env GOPATH) + +all:tests + +tests: $(OUT) + +%:%.go + make -C ../testdata/schema + @echo "Building $@ ..." + GOPATH=$(GOPATH) $(GO) build -gcflags="all=-N -l" $< + +clean: + @echo "Removing files ..." + rm -rf $(OUT) diff --git a/src/cvl/tests/acl_rule.json b/src/cvl/tests/acl_rule.json new file mode 100644 index 0000000000..cb8bf4e935 --- /dev/null +++ b/src/cvl/tests/acl_rule.json @@ -0,0 +1,20 @@ +{ +"ACL_TABLE": { + "TestACL11": { + "type": "L3", + "ports": "Ethernet0" + } + }, +"ACL_RULE": { + "TestACL11|Rule1": { + "PRIORITY": "55", + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "0" + }, + "TestACL11|Rule2": { + "PRIORITY": "55", + "PACKET_ACTION": "DROP", + "L4_SRC_PORT": "1" + } + } +} diff --git a/src/cvl/tests/cfg_validator.go b/src/cvl/tests/cfg_validator.go new file mode 100644 index 0000000000..f5a532da95 --- /dev/null +++ b/src/cvl/tests/cfg_validator.go @@ -0,0 +1,273 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "os" + "time" + "io/ioutil" + "cvl" +) + +func main() { + jsonData :=`{ + "VLAN": { + "Vlan100": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + }, + "Vlan1200": { + "members": [ + "Ethernet64", + "Ethernet8" + ], + "vlanid": "1200" + }, + "Vlan2500": { + "members": [ + "Ethernet8", + "Ethernet64" + ], + "vlanid": "2500" + } + } + }` + /* + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "l3" + } + }, + "ACL_RULE": { + "TestACL1|Rule1": { + "packet_action": "forward", + "src_ip": "10.1.1.1/32", + "l4_src_port": "ABC", + "ip_protocol": "ip", + "dst_ip": "20.2.2.2/32", + "l4_dst_port_range": "9000-12000", + "mirror_action" : "mirror1" + } + }*/ + +/*jsonData := `{ + "DEVICE_METADATA": { + "localhost": { + "hwsku": "Force10-S6100", + "default_bgp_status": "up", + "docker_routing_config_mode": "unified", + "hostname": "sonic-s6100-01", + "platform": "x86_64-dell_s6100_c2538-r0", + "mac": "4c:76:25:f4:70:82", + "default_pfcwd_status": "disable", + "deployment_id": "1", + "type": "ToRRouter" + } + } + }`*/ +/*jsonData := `{ + "DEVICE_NEIGHBOR": { + "ARISTA04T1": { + "mgmt_addr": "10.20.0.163", + "hwsku": "Arista", + "lo_addr": "2.2.2.2", + "local_port": "Ethernet124", + "type": "LeafRouter", + "port": "Ethernet68" + } + } + }`*/ +/*jsonData := `{ + "BGP_NEIGHBOR": { + "10.0.0.61": { + "local_addr": "10.0.0.60", + "asn": 64015, + "name": "ARISTA15T0" + } + } + }`*/ + +/* jsonData := `{ + "INTERFACE": { + "Ethernet68|10.0.0.0/31": {}, + "Ethernet24|10.0.0.2/31": {}, + "Ethernet112|10.0.0.4/31": {} + } + }`*/ + +/*jsonData := `{ + "INTERFACE": { + "Ethernet68|10.0.0.0/31": {}, + "Ethernet24|10.0.0.2/31": {}, + "Ethernet112|10.0.0.4/31": {} + } + }`*/ +/*jsonData := `{ + "PORTCHANNEL_INTERFACE": { + "PortChannel01|10.0.0.56/31": {}, + "PortChannel01|FC00::71/126": {}, + "PortChannel02|10.0.0.58/31": {}, + "PortChannel02|FC00::75/126": {} + } + + }`*/ +/*jsonData := `{ + "VLAN_INTERFACE": { + "Vlan1000|192.168.0.1/27": {} + } + }`*/ + start := time.Now() + + dataFile := "" + if (len(os.Args) >= 2) { + if (os.Args[1] == "debug") { + cvl.Debug(true) + } else { + dataFile = os.Args[1] + } + } + if (len(os.Args) == 3) { + dataFile = os.Args[2] + } + + //cvl.Initialize() + + b, e := ioutil.ReadFile(dataFile) + if e != nil { + fmt.Printf("\nFailed to read data file : %v\n", e) + } else { + jsonData = string(b) + } + + + cv, ret := cvl.ValidationSessOpen() + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("NewDB: Could not create CVL session") + return + } + + err := cv.ValidateConfig(jsonData) + + fmt.Printf("\nValidating data = %v\n\n", jsonData); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + + keyData := make([]cvl.CVLEditConfigData, 4) + keyData[0].VType = cvl.VALIDATE_NONE + keyData[0].VOp = cvl.OP_NONE + keyData[0].Key = "ACL_TABLE|MyACL55_ACL_IPV4" + keyData[0].Data = make(map[string]string) + keyData[0].Data["stage"] = "INGRESS" + keyData[0].Data["type"] = "l3" + + keyData[1].VType = cvl.VALIDATE_NONE + keyData[1].VOp = cvl.OP_NONE + keyData[1].Key = "ACL_RULE|MyACL55_ACL_IPV4|RULE_1" + keyData[1].Data = make(map[string]string) + keyData[1].Data["packet_action"] = "forward" + keyData[1].Data["ip_protocol"] = "ip" + keyData[1].Data["src_ip"] = "10.1.1.1/32" + keyData[1].Data["dst_ip"] = "20.2.2.2/32" + + keyData[2].VType = cvl.VALIDATE_NONE + keyData[2].VOp = cvl.OP_NONE + keyData[2].Key = "ACL_TABLE|MyACL11_ACL_IPV4" + keyData[2].Data = make(map[string]string) + keyData[2].Data["stage"] = "INGRESS" + + keyData[3].VType = cvl.VALIDATE_ALL + keyData[3].VOp = cvl.OP_CREATE + keyData[3].Key = "VLAN|Vlan901" + keyData[3].Data = make(map[string]string) + keyData[3].Data["members"] = "Ethernet8" + keyData[3].Data["vlanid"] = "901" + + _, ret = cv.ValidateEditConfig(keyData) + fmt.Printf("\n\n\n cvl.ValidateEditConfig() = %d\n", ret) + + keyData1 := make([]cvl.CVLEditConfigData, 3) + keyData1[0].VType = cvl.VALIDATE_NONE + keyData1[0].VOp = cvl.OP_NONE + keyData1[0].Key = "ACL_TABLE|MyACL11_ACL_IPV4" + keyData1[0].Data = make(map[string]string) + keyData1[0].Data["stage"] = "INGRESS" + keyData1[0].Data["type"] = "l3" + + keyData1[1].VType = cvl.VALIDATE_NONE + keyData1[1].VOp = cvl.OP_NONE + keyData1[1].Key = "ACL_RULE|MyACL11_ACL_IPV4|RULE_1" + keyData1[1].Data = make(map[string]string) + keyData1[1].Data["packet_action"] = "forward" + keyData1[1].Data["ip_protocol"] = "ip" + keyData1[1].Data["src_ip"] = "10.1.1.1/32" + keyData1[1].Data["dst_ip"] = "20.2.2.2/32" + + keyData1[2].VType = cvl.VALIDATE_ALL + keyData1[2].VOp = cvl.OP_UPDATE + keyData1[2].Key = "ACL_TABLE|MyACL33_ACL_IPV4" + keyData1[2].Data = make(map[string]string) + keyData1[2].Data["stage"] = "INGRESS" + + _, ret = cv.ValidateEditConfig(keyData) + fmt.Printf("\n\n\n cvl.ValidateEditConfig() = %d\n", ret) + + + keyData2 := make([]cvl.CVLEditConfigData, 3) + keyData2[0].VType = cvl.VALIDATE_ALL + keyData2[0].VOp = cvl.OP_DELETE + keyData2[0].Key = "ACL_TABLE|MyACL11_ACL_IPV4" + keyData2[0].Data = make(map[string]string) + + keyData2[1].VType = cvl.VALIDATE_ALL + keyData2[1].VOp = cvl.OP_DELETE + keyData2[1].Key = "ACL_RULE|MyACL11_ACL_IPV4|RULE_1" + keyData2[1].Data = make(map[string]string) + + keyData2[2].VType = cvl.VALIDATE_ALL + keyData2[2].VOp = cvl.OP_DELETE + keyData2[2].Key = "ACL_TABLE|MyACL33_ACL_IPV4" + keyData2[2].Data = make(map[string]string) + + _, ret = cv.ValidateEditConfig(keyData) + fmt.Printf("\n\n\n cvl.ValidateEditConfig() = %d\n", ret) + + + cvl.ValidationSessClose(cv) + cvl.Finish() + fmt.Printf("\n\n\n Time taken = %v\n", time.Since(start)) + + stopChan := make(chan int, 1) + for { + select { + case <- stopChan: + } + } + + +} diff --git a/src/cvl/tests/config_db.json b/src/cvl/tests/config_db.json new file mode 100644 index 0000000000..381412ad6b --- /dev/null +++ b/src/cvl/tests/config_db.json @@ -0,0 +1,107 @@ +{ + "VLAN": { + "Vlan100": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + }, + "Vlan1200": { + "members": [ + "Ethernet64", + "Ethernet8" + ], + "vlanid": "1200" + }, + "Vlan2500": { + "members": [ + "Ethernet8", + "Ethernet64" + ], + "vlanid": "2500" + } + }, + "VLAN_MEMBER": { + "Vlan100|Ethernet924": { + "tagging_mode": "tagged" + }, + "Vlan100|Ethernet28": { + "tagging_mode": "tagged" + }, + "Vlan1200|Ethernet4": { + "tagging_mode": "tagged" + } + }, + "WRED_PROFILE": { + "AZURE_LOSSLESS": { + "red_max_threshold": "312000", + "wred_green_enable": "true", + "ecn": "ecn_all", + "green_min_threshold": "104000", + "red_min_threshold": "104000", + "wred_yellow_enable": "true", + "yellow_min_threshold": "104000", + "wred_red_enable": "true", + "yellow_max_threshold": "312000", + "green_max_threshold": "312000" + } + }, + "BUFFER_POOL": { + "egress_lossless_pool": { + "type": "egress", + "mode": "static", + "size": "12766208" + }, + "egress_lossy_pool": { + "type": "egress", + "mode": "dynamic", + "size": "8072396" + }, + "ingress_lossless_pool": { + "type": "ingress", + "mode": "dynamic", + "size": "12766208" + } + }, + "MIRROR_SESSION": { + "everflow0": { + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2" + } + }, + "SCHEDULER": { + "scheduler.0": { + "type": "DWRR", + "weight": "25" + }, + "scheduler.1": { + "type": "DWRR", + "weight": "30" + }, + "scheduler.2": { + "type": "DWRR", + "weight": "20" + } + }, + "QUEUE": { + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|0": { + "scheduler": "[SCHEDULER|scheduler.1]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|1": { + "scheduler": "[SCHEDULER|scheduler.2]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|3-4": { + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]", + "scheduler": "[SCHEDULER|scheduler.0]" + } + }, + "TC_TO_QUEUE_MAP": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "4": "4" + } + } +} diff --git a/src/cvl/tests/config_db1.json b/src/cvl/tests/config_db1.json new file mode 100644 index 0000000000..b565af7cb1 --- /dev/null +++ b/src/cvl/tests/config_db1.json @@ -0,0 +1,313 @@ +{ + "QUEUE": { + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|0": { + "scheduler": "[SCHEDULER|scheduler.1]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|1": { + "scheduler": "[SCHEDULER|scheduler.2]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|3-4": { + "wred_profile": "[WRED_PROFILE|AZURE_LOSSLESS]", + "scheduler": "[SCHEDULER|scheduler.0]" + } + }, + "PORT": { + "Ethernet0": { + "alias": "fortyGigE0/0", + "lanes": "29,30,31,32" + }, + "Ethernet4": { + "alias": "fortyGigE0/4", + "lanes": "25,26,27,28" + }, + "Ethernet8": { + "alias": "fortyGigE0/8", + "lanes": "37,38,39,40" + }, + "Ethernet12": { + "alias": "fortyGigE0/12", + "lanes": "33,34,35,36" + }, + "Ethernet16": { + "alias": "fortyGigE0/16", + "lanes": "41,42,43,44" + }, + "Ethernet20": { + "alias": "fortyGigE0/20", + "lanes": "45,46,47,48" + }, + "Ethernet24": { + "alias": "fortyGigE0/24", + "lanes": "5,6,7,8" + }, + "Ethernet28": { + "alias": "fortyGigE0/28", + "lanes": "1,2,3,4" + }, + "Ethernet32": { + "alias": "fortyGigE0/32", + "lanes": "9,10,11,12" + }, + "Ethernet36": { + "alias": "fortyGigE0/36", + "lanes": "13,14,15,16" + }, + "Ethernet40": { + "alias": "fortyGigE0/40", + "lanes": "21,22,23,24" + }, + "Ethernet44": { + "alias": "fortyGigE0/44", + "lanes": "17,18,19,20" + }, + "Ethernet48": { + "alias": "fortyGigE0/48", + "lanes": "49,50,51,52" + }, + "Ethernet52": { + "alias": "fortyGigE0/52", + "lanes": "53,54,55,56" + }, + "Ethernet56": { + "alias": "fortyGigE0/56", + "lanes": "61,62,63,64" + }, + "Ethernet60": { + "alias": "fortyGigE0/60", + "lanes": "57,58,59,60" + }, + "Ethernet64": { + "alias": "fortyGigE0/64", + "lanes": "65,66,67,68" + }, + "Ethernet68": { + "alias": "fortyGigE0/68", + "lanes": "69,70,71,72" + }, + "Ethernet72": { + "alias": "fortyGigE0/72", + "lanes": "77,78,79,80" + }, + "Ethernet76": { + "alias": "fortyGigE0/76", + "lanes": "73,74,75,76" + }, + "Ethernet80": { + "alias": "fortyGigE0/80", + "lanes": "105,106,107,108" + }, + "Ethernet84": { + "alias": "fortyGigE0/84", + "lanes": "109,110,111,112" + }, + "Ethernet88": { + "alias": "fortyGigE0/88", + "lanes": "117,118,119,120" + }, + "Ethernet92": { + "alias": "fortyGigE0/92", + "lanes": "113,114,115,116" + }, + "Ethernet96": { + "alias": "fortyGigE0/96", + "lanes": "121,122,123,124" + }, + "Ethernet100": { + "alias": "fortyGigE0/100", + "lanes": "125,126,127,128" + }, + "Ethernet104": { + "alias": "fortyGigE0/104", + "lanes": "85,86,87,88" + }, + "Ethernet108": { + "alias": "fortyGigE0/108", + "lanes": "81,82,83,84" + }, + "Ethernet112": { + "alias": "fortyGigE0/112", + "lanes": "89,90,91,92" + }, + "Ethernet116": { + "alias": "fortyGigE0/116", + "lanes": "93,94,95,96" + }, + "Ethernet120": { + "alias": "fortyGigE0/120", + "lanes": "97,98,99,100" + }, + "Ethernet124": { + "alias": "fortyGigE0/124", + "lanes": "101,102,103,104" + } + }, + "WRED_PROFILE": { + "AZURE_LOSSLESS": { + "red_max_threshold": "312000", + "wred_green_enable": "true", + "ecn": "ecn_all", + "green_min_threshold": "104000", + "red_min_threshold": "104000", + "wred_yellow_enable": "true", + "yellow_min_threshold": "104000", + "wred_red_enable": "true", + "yellow_max_threshold": "312000", + "green_max_threshold": "312000" + } + }, + "SCHEDULER": { + "scheduler.0": { + "type": "DWRR", + "weight": "25" + }, + "scheduler.1": { + "type": "DWRR", + "weight": "30" + }, + "scheduler.2": { + "type": "DWRR", + "weight": "20" + } + }, + "BUFFER_PG": { + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|0-1": { + "profile": "[BUFFER_PROFILE|ingress_lossy_profile]" + }, + "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28,Ethernet32,Ethernet36,Ethernet40,Ethernet44,Ethernet48,Ethernet52,Ethernet56,Ethernet60,Ethernet64,Ethernet68,Ethernet72,Ethernet76,Ethernet80,Ethernet84,Ethernet88,Ethernet92,Ethernet96,Ethernet100,Ethernet104,Ethernet108,Ethernet112,Ethernet116,Ethernet120,Ethernet124|3-4": { + "profile": "[BUFFER_PROFILE|ingress_lossless_profile]" + } + }, + "BUFFER_PROFILE": { + "egress_lossless_profile": { + "static_th": "12766208", + "pool": "[BUFFER_POOL|egress_lossless_pool]", + "size": "0" + }, + "egress_lossy_profile": { + "dynamic_th": "3", + "pool": "[BUFFER_POOL|egress_lossy_pool]", + "size": "1518" + }, + "ingress_lossless_profile": { + "xon_offset": "2496", + "dynamic_th": "-4", + "xon": "18432", + "xoff": "40560", + "pool": "[BUFFER_POOL|ingress_lossless_pool]", + "size": "41808" + }, + "ingress_lossy_profile": { + "dynamic_th": "3", + "pool": "[BUFFER_POOL|ingress_lossless_pool]", + "size": "0" + } + }, + "BUFFER_POOL": { + "egress_lossless_pool": { + "type": "egress", + "mode": "static", + "size": "12766208" + }, + "egress_lossy_pool": { + "type": "egress", + "mode": "dynamic", + "size": "8072396" + }, + "ingress_lossless_pool": { + "type": "ingress", + "mode": "dynamic", + "size": "12766208" + } + }, + "MIRROR_SESSION": { + "everflow0": { + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2" + } + }, + "TC_TO_QUEUE_MAP": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "4": "4" + } + }, + "DSCP_TO_TC_MAP": { + "AZURE": { + "56": "0", + "54": "0", + "28": "0", + "48": "0", + "29": "0", + "60": "0", + "61": "0", + "62": "0", + "63": "0", + "49": "0", + "34": "0", + "24": "0", + "25": "0", + "26": "0", + "27": "0", + "20": "0", + "21": "0", + "22": "0", + "23": "0", + "46": "0", + "47": "0", + "44": "0", + "45": "0", + "42": "0", + "43": "0", + "40": "0", + "41": "0", + "1": "0", + "0": "0", + "3": "3", + "2": "0", + "5": "0", + "4": "4", + "7": "0", + "6": "0", + "9": "0", + "8": "1", + "35": "0", + "13": "0", + "12": "0", + "15": "0", + "58": "0", + "11": "0", + "10": "0", + "39": "0", + "38": "0", + "59": "0", + "14": "0", + "17": "0", + "16": "0", + "19": "0", + "18": "0", + "31": "0", + "30": "0", + "51": "0", + "36": "0", + "53": "0", + "52": "0", + "33": "0", + "55": "0", + "37": "0", + "32": "0", + "57": "0", + "50": "0" + } + }, + "TC_TO_PRIORITY_GROUP_MAP": { + "AZURE": { + "1": "1", + "0": "0", + "3": "3", + "4": "4" + } + } +} + diff --git a/src/cvl/tests/config_db2.json b/src/cvl/tests/config_db2.json new file mode 100644 index 0000000000..e6be83ac4e --- /dev/null +++ b/src/cvl/tests/config_db2.json @@ -0,0 +1,3437 @@ +{ + "VLAN_MEMBER": { + "Vlan51|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan51|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan97|Ethernet4": { + "tagging_mode": "untagged" + }, + "Vlan99|Ethernet4": { + "tagging_mode": "untagged" + }, + "Vlan99|Ethernet108": { + "tagging_mode": "untagged" + }, + "Vlan99|Ethernet124": { + "tagging_mode": "untagged" + }, + "Vlan101|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan101|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan101|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan102|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan102|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan102|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan103|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan103|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan103|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan104|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan104|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan104|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan105|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan105|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan105|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan106|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan106|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan106|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan107|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan107|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan107|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan108|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan108|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan108|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan109|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan109|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan109|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan110|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan110|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan110|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan111|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan111|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan111|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan112|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan112|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan112|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan113|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan113|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan113|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan114|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan114|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan114|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan115|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan115|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan115|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan116|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan116|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan116|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan117|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan117|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan117|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan118|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan118|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan118|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan119|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan119|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan119|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan120|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan120|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan120|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan121|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan121|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan121|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan122|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan122|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan122|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan123|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan123|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan123|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan124|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan124|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan124|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan125|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan125|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan125|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan126|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan126|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan126|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan127|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan127|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan127|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan128|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan128|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan128|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan129|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan129|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan129|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan130|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan130|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan130|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan131|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan131|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan131|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan132|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan132|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan132|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan133|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan133|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan133|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan134|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan134|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan134|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan135|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan135|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan135|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan136|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan136|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan136|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan137|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan137|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan137|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan138|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan138|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan138|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan139|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan139|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan139|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan140|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan140|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan140|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan141|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan141|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan141|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan142|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan142|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan142|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan143|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan143|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan143|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan144|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan144|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan144|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan145|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan145|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan145|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan146|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan146|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan146|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan147|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan147|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan147|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan148|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan148|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan148|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan149|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan149|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan149|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan150|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan150|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan150|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan151|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan151|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan151|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan152|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan152|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan152|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan153|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan153|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan153|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan154|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan154|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan154|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan155|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan155|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan155|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan156|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan156|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan156|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan157|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan157|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan157|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan158|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan158|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan158|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan159|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan159|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan159|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan160|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan160|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan160|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan161|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan161|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan161|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan162|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan162|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan162|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan163|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan163|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan163|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan164|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan164|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan164|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan165|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan165|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan165|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan166|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan166|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan166|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan167|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan167|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan167|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan168|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan168|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan168|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan169|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan169|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan169|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan170|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan170|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan170|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan171|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan171|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan171|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan172|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan172|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan172|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan173|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan173|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan173|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan174|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan174|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan174|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan175|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan175|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan175|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan176|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan176|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan176|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan177|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan177|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan177|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan178|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan178|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan178|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan179|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan179|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan179|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan180|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan180|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan180|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan181|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan181|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan181|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan182|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan182|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan182|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan183|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan183|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan183|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan184|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan184|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan184|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan185|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan185|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan185|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan186|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan186|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan186|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan187|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan187|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan187|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan188|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan188|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan188|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan189|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan189|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan189|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan190|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan190|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan190|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan191|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan191|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan191|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan192|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan192|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan192|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan193|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan193|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan193|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan194|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan194|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan194|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan195|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan195|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan195|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan196|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan196|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan196|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan197|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan197|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan197|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan198|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan198|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan198|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan199|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan200|Ethernet64": { + "tagging_mode": "tagged" + }, + "Vlan200|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan200|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan201|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan201|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan201|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan202|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan202|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan202|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan203|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan203|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan203|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan204|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan204|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan204|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan205|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan205|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan205|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan206|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan206|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan206|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan207|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan207|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan207|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan208|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan208|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan208|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan209|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan209|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan209|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan210|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan210|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan210|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan211|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan211|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan211|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan212|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan212|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan212|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan213|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan213|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan213|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan214|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan214|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan214|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan215|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan215|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan215|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan216|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan216|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan216|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan217|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan217|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan217|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan218|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan218|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan218|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan219|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan219|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan219|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan220|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan220|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan220|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan221|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan221|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan221|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan222|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan222|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan222|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan223|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan223|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan223|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan224|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan224|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan224|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan225|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan225|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan225|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan226|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan226|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan226|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan227|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan227|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan227|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan228|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan228|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan228|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan229|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan229|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan229|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan230|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan230|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan230|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan231|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan231|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan231|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan232|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan232|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan232|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan233|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan233|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan233|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan234|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan234|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan234|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan235|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan235|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan235|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan236|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan236|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan236|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan237|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan237|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan237|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan238|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan238|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan238|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan239|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan239|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan239|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan240|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan240|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan240|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan241|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan241|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan241|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan242|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan242|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan242|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan243|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan243|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan243|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan244|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan244|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan244|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan245|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan245|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan245|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan246|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan246|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan246|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan247|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan247|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan247|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan248|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan248|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan248|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan249|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan249|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan249|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan250|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan250|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan250|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan251|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan251|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan251|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan252|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan252|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan252|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan253|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan253|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan253|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan254|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan254|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan254|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan255|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan255|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan255|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan256|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan256|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan256|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan257|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan257|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan257|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan258|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan258|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan258|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan259|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan259|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan259|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan260|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan260|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan260|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan261|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan261|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan261|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan262|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan262|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan262|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan263|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan263|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan263|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan264|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan264|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan264|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan265|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan265|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan265|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan266|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan266|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan266|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan267|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan267|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan267|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan268|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan268|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan268|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan269|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan269|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan269|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan270|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan270|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan270|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan271|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan271|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan271|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan272|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan272|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan272|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan273|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan273|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan273|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan274|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan274|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan274|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan275|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan275|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan275|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan276|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan276|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan276|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan277|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan277|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan277|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan278|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan278|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan278|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan279|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan279|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan279|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan280|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan280|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan280|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan281|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan281|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan281|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan282|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan282|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan282|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan283|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan283|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan283|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan284|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan284|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan284|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan285|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan285|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan285|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan286|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan286|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan286|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan287|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan287|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan287|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan288|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan288|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan288|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan289|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan289|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan289|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan290|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan290|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan290|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan291|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan291|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan291|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan292|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan292|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan292|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan293|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan293|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan293|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan294|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan294|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan294|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan295|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan295|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan295|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan296|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan296|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan296|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan297|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan297|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan297|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan298|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan298|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan298|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan299|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan299|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan299|Ethernet4": { + "tagging_mode": "tagged" + }, + "Vlan300|Ethernet108": { + "tagging_mode": "tagged" + }, + "Vlan300|Ethernet124": { + "tagging_mode": "tagged" + }, + "Vlan300|Ethernet4": { + "tagging_mode": "tagged" + } + }, + "VLAN": { + "Vlan51": { + "members": [ + "Ethernet108", + "Ethernet124" + ], + "vlanid": "51" + }, + "Vlan97": { + "members": [ + "Ethernet4" + ], + "vlanid": "97" + }, + "Vlan99": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "99" + }, + "Vlan101": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "101" + }, + "Vlan102": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "102" + }, + "Vlan103": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "103" + }, + "Vlan104": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "104" + }, + "Vlan105": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "105" + }, + "Vlan106": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "106" + }, + "Vlan107": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "107" + }, + "Vlan108": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "108" + }, + "Vlan109": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "109" + }, + "Vlan110": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "110" + }, + "Vlan111": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "111" + }, + "Vlan112": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "112" + }, + "Vlan113": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "113" + }, + "Vlan114": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "114" + }, + "Vlan115": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "115" + }, + "Vlan116": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "116" + }, + "Vlan117": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "117" + }, + "Vlan118": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "118" + }, + "Vlan119": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "119" + }, + "Vlan120": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "120" + }, + "Vlan121": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "121" + }, + "Vlan122": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "122" + }, + "Vlan123": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "123" + }, + "Vlan124": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "124" + }, + "Vlan125": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "125" + }, + "Vlan126": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "126" + }, + "Vlan127": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "127" + }, + "Vlan128": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "128" + }, + "Vlan129": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "129" + }, + "Vlan130": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "130" + }, + "Vlan131": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "131" + }, + "Vlan132": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "132" + }, + "Vlan133": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "133" + }, + "Vlan134": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "134" + }, + "Vlan135": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "135" + }, + "Vlan136": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "136" + }, + "Vlan137": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "137" + }, + "Vlan138": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "138" + }, + "Vlan139": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "139" + }, + "Vlan140": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "140" + }, + "Vlan141": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "141" + }, + "Vlan142": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "142" + }, + "Vlan143": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "143" + }, + "Vlan144": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "144" + }, + "Vlan145": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "145" + }, + "Vlan146": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "146" + }, + "Vlan147": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "147" + }, + "Vlan148": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "148" + }, + "Vlan149": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "149" + }, + "Vlan150": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "150" + }, + "Vlan151": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "151" + }, + "Vlan152": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "152" + }, + "Vlan153": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "153" + }, + "Vlan154": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "154" + }, + "Vlan155": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "155" + }, + "Vlan156": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "156" + }, + "Vlan157": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "157" + }, + "Vlan158": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "158" + }, + "Vlan159": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "159" + }, + "Vlan160": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "160" + }, + "Vlan161": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "161" + }, + "Vlan162": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "162" + }, + "Vlan163": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "163" + }, + "Vlan164": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "164" + }, + "Vlan165": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "165" + }, + "Vlan166": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "166" + }, + "Vlan167": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "167" + }, + "Vlan168": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "168" + }, + "Vlan169": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "169" + }, + "Vlan170": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "170" + }, + "Vlan171": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "171" + }, + "Vlan172": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "172" + }, + "Vlan173": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "173" + }, + "Vlan174": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "174" + }, + "Vlan175": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "175" + }, + "Vlan176": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "176" + }, + "Vlan177": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "177" + }, + "Vlan178": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "178" + }, + "Vlan179": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "179" + }, + "Vlan180": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "180" + }, + "Vlan181": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "181" + }, + "Vlan182": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "182" + }, + "Vlan183": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "183" + }, + "Vlan184": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "184" + }, + "Vlan185": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "185" + }, + "Vlan186": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "186" + }, + "Vlan187": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "187" + }, + "Vlan188": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "188" + }, + "Vlan189": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "189" + }, + "Vlan190": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "190" + }, + "Vlan191": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "191" + }, + "Vlan192": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "192" + }, + "Vlan193": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "193" + }, + "Vlan194": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "194" + }, + "Vlan195": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "195" + }, + "Vlan196": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "196" + }, + "Vlan197": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "197" + }, + "Vlan198": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "198" + }, + "Vlan199": { + "members": [ + "Ethernet64" + ], + "vlanid": "199" + }, + "Vlan200": { + "members": [ + "Ethernet64", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "200" + }, + "Vlan201": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "201" + }, + "Vlan202": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "202" + }, + "Vlan203": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "203" + }, + "Vlan204": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "204" + }, + "Vlan205": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "205" + }, + "Vlan206": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "206" + }, + "Vlan207": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "207" + }, + "Vlan208": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "208" + }, + "Vlan209": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "209" + }, + "Vlan210": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "210" + }, + "Vlan211": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "211" + }, + "Vlan212": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "212" + }, + "Vlan213": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "213" + }, + "Vlan214": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "214" + }, + "Vlan215": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "215" + }, + "Vlan216": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "216" + }, + "Vlan217": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "217" + }, + "Vlan218": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "218" + }, + "Vlan219": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "219" + }, + "Vlan220": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "220" + }, + "Vlan221": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "221" + }, + "Vlan222": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "222" + }, + "Vlan223": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "223" + }, + "Vlan224": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "224" + }, + "Vlan225": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "225" + }, + "Vlan226": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "226" + }, + "Vlan227": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "227" + }, + "Vlan228": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "228" + }, + "Vlan229": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "229" + }, + "Vlan230": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "230" + }, + "Vlan231": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "231" + }, + "Vlan232": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "232" + }, + "Vlan233": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "233" + }, + "Vlan234": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "234" + }, + "Vlan235": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "235" + }, + "Vlan236": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "236" + }, + "Vlan237": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "237" + }, + "Vlan238": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "238" + }, + "Vlan239": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "239" + }, + "Vlan240": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "240" + }, + "Vlan241": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "241" + }, + "Vlan242": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "242" + }, + "Vlan243": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "243" + }, + "Vlan244": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "244" + }, + "Vlan245": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "245" + }, + "Vlan246": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "246" + }, + "Vlan247": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "247" + }, + "Vlan248": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "248" + }, + "Vlan249": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "249" + }, + "Vlan250": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "250" + }, + "Vlan251": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "251" + }, + "Vlan252": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "252" + }, + "Vlan253": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "253" + }, + "Vlan254": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "254" + }, + "Vlan255": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "255" + }, + "Vlan256": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "256" + }, + "Vlan257": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "257" + }, + "Vlan258": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "258" + }, + "Vlan259": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "259" + }, + "Vlan260": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "260" + }, + "Vlan261": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "261" + }, + "Vlan262": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "262" + }, + "Vlan263": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "263" + }, + "Vlan264": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "264" + }, + "Vlan265": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "265" + }, + "Vlan266": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "266" + }, + "Vlan267": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "267" + }, + "Vlan268": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "268" + }, + "Vlan269": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "269" + }, + "Vlan270": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "270" + }, + "Vlan271": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "271" + }, + "Vlan272": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "272" + }, + "Vlan273": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "273" + }, + "Vlan274": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "274" + }, + "Vlan275": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "275" + }, + "Vlan276": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "276" + }, + "Vlan277": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "277" + }, + "Vlan278": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "278" + }, + "Vlan279": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "279" + }, + "Vlan280": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "280" + }, + "Vlan281": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "281" + }, + "Vlan282": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "282" + }, + "Vlan283": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "283" + }, + "Vlan284": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "284" + }, + "Vlan285": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "285" + }, + "Vlan286": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "286" + }, + "Vlan287": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "287" + }, + "Vlan288": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "288" + }, + "Vlan289": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "289" + }, + "Vlan290": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "290" + }, + "Vlan291": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "291" + }, + "Vlan292": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "292" + }, + "Vlan293": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "293" + }, + "Vlan294": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "294" + }, + "Vlan295": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "295" + }, + "Vlan296": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "296" + }, + "Vlan297": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "297" + }, + "Vlan298": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "298" + }, + "Vlan299": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "299" + }, + "Vlan300": { + "members": [ + "Ethernet4", + "Ethernet108", + "Ethernet124" + ], + "vlanid": "300" + } + } +} diff --git a/src/cvl/tests/create_acl_table.json b/src/cvl/tests/create_acl_table.json new file mode 100644 index 0000000000..604be2e2d2 --- /dev/null +++ b/src/cvl/tests/create_acl_table.json @@ -0,0 +1,8 @@ +{ +"ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + } +} diff --git a/src/cvl/tests/cv_acl.go b/src/cvl/tests/cv_acl.go new file mode 100644 index 0000000000..cb12c0109b --- /dev/null +++ b/src/cvl/tests/cv_acl.go @@ -0,0 +1,443 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "time" + "os" + "cvl" + "github.com/go-redis/redis" + "strconv" +) + +func getConfigDbClient() *redis.Client { + rclient := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 4, + DialTimeout: 0, + }) + _, err := rclient.Ping().Result() + if err != nil { + fmt.Printf("failed to connect to redis server %v", err) + } + return rclient +} + +/* Unloads the Config DB based on JSON File. */ +func unloadConfigDB(rclient *redis.Client, key string, data map[string]string) { + _, err := rclient.Del(key).Result() + + if err != nil { + fmt.Printf("Failed to delete for key %s, data %v, err %v", key, data, err) + } + +} + +/* Loads the Config DB based on JSON File. */ +func loadConfigDB(rclient *redis.Client, key string, data map[string]string) { + + dataTmp := make(map[string]interface{}) + + for k, v := range data { + dataTmp[k] = v + } + + _, err := rclient.HMSet(key, dataTmp).Result() + + if err != nil { + fmt.Printf("Failed to add for key %s, data %v, err %v", key, data, err) + } + +} + +func main() { + start := time.Now() + count := 0 + + cvl.Initialize() + + if ((len(os.Args) > 1) && (os.Args[1] == "debug")) { + cvl.Debug(true) + } + + rclient := getConfigDbClient() + + if ((len(os.Args) > 1) && (os.Args[1] == "add")) { + + //Add ACL + aclNoStart, _ := strconv.Atoi(os.Args[2]) + aclNoEnd, _ := strconv.Atoi(os.Args[3]) + for aclNum:= aclNoStart ;aclNum <= aclNoEnd; aclNum++ { + aclNo := fmt.Sprintf("%d", aclNum) + + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataAclRule := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + fmt.Sprintf("ACL_TABLE|TestACL%s", aclNo), + map[string]string { + "stage": "INGRESS", + "type": "L3", + //"ports@": "Ethernet0", + }, + }, + } + + _, ret := cvSess.ValidateEditConfig(cfgDataAclRule) + + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + return + } + + cfgDataAclRule[0].VType = cvl.VALIDATE_NONE + + //Create 7 ACL rules + for i:=0; i<7; i++ { + cfgDataAclRule = append(cfgDataAclRule, cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + fmt.Sprintf("ACL_RULE|TestACL%s|Rule%d", aclNo, i+1), + map[string]string { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPV4", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": fmt.Sprintf("%d", 201 + i), + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT": fmt.Sprintf("%d", 701 + i), + }, + }) + + _, ret1 := cvSess.ValidateEditConfig(cfgDataAclRule) + if (ret1 != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + return + } + + cfgDataAclRule[1 + i].VType = cvl.VALIDATE_NONE + } + + //Write to DB + for _, cfgDataItem := range cfgDataAclRule { + loadConfigDB(rclient, cfgDataItem.Key, cfgDataItem.Data) + } + + cvl.ValidationSessClose(cvSess) + } + + return + } else if ((len(os.Args) > 1) && (os.Args[1] == "del")) { + aclNoStart, _ := strconv.Atoi(os.Args[2]) + aclNoEnd, _ := strconv.Atoi(os.Args[3]) + for aclNum:= aclNoStart ;aclNum <= aclNoEnd; aclNum++ { + aclNo := fmt.Sprintf("%d", aclNum) + cvSess,_ := cvl.ValidationSessOpen() + + //Delete ACL + + cfgDataAclRule := []cvl.CVLEditConfigData{} + + //Create 7 ACL rules + for i:=0; i<7; i++ { + cfgDataAclRule = append(cfgDataAclRule, cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + fmt.Sprintf("ACL_RULE|TestACL%s|Rule%d", aclNo, i+1), + map[string]string { + }, + }) + + _, ret := cvSess.ValidateEditConfig(cfgDataAclRule) + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + return + } + + cfgDataAclRule[i].VType = cvl.VALIDATE_NONE + } + + cfgDataAclRule = append(cfgDataAclRule, cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + fmt.Sprintf("ACL_TABLE|TestACL%s", aclNo), + map[string]string { + }, + }) + + _, ret := cvSess.ValidateEditConfig(cfgDataAclRule) + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + return + } + + //Write to DB + for _, cfgDataItem := range cfgDataAclRule { + unloadConfigDB(rclient, cfgDataItem.Key, cfgDataItem.Data) + } + + cvl.ValidationSessClose(cvSess) + } + + return + } + + cv, ret := cvl.ValidationSessOpen() + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Could not create CVL session") + return + } + + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + }, + "TestACL2": { + "stage": "EGRESS", + "ports": "Ethernet4" + } + } + }` + + fmt.Printf("\nValidating data = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL2": { + "stage": "EGRESS", + "ports": "Ethernet804" + } + } + }` + + + fmt.Printf("\nValidating data for external dependency check = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "type": "L3" + } + } + }` + + + fmt.Printf("\nValidating data for mandatory element misssing = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + }, + "ACL_RULE": { + "TestACL1|Rule1": { + "PACKET_ACTION": "FORWARD", + "IP_PROTOCOL": "103", + "SRC_IP": "10.1.1.1/32", + "DST_IP": "20.2.2.2/32" + } + } + }` + + + + fmt.Printf("\nValidating data for internal dependency check = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + }, + "ACL_RULE": { + "TestACL1|Rule1": { + "PACKET_ACTION": "FORWARD", + "IP_PROTOCOL": "103" + } + } + }` + + + + fmt.Printf("\nValidating data for mandatory element check = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + }, + "ACL_RULE": { + "TestACL1|Rule1": { + "PACKET_ACTION": "FORWARD", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32" + } + } + }` + + + + fmt.Printf("\nValidating data for mandatory element check = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + }, + "ACL_RULE": { + "TestACL1|Rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": 8080, + "ETHER_TYPE":"0x0800", + "IP_PROTOCOL": "1", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000" + } + } + }` + + + + fmt.Printf("\nValidating data for pattern check = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "ACL_TABLE": { + "TestACL1": { + "stage": "INGRESS", + "type": "L3" + } + }, + "ACL_RULE": { + "TestACL1|Rule1": { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "ABC", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000" + } + } + }` + + + + fmt.Printf("\nValidating data for type check = %v\n\n", jsonData); + + err := cv.ValidateConfig(jsonData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + + cvl.ValidationSessClose(cv) + + cvl.Finish() + + fmt.Printf("\n\n\n Time taken for %v requests = %v\n", count, time.Since(start)) +} diff --git a/src/cvl/tests/cv_edit_op.go b/src/cvl/tests/cv_edit_op.go new file mode 100644 index 0000000000..a9035bcf51 --- /dev/null +++ b/src/cvl/tests/cv_edit_op.go @@ -0,0 +1,192 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "time" + "os" + "cvl" +) + +func main() { + start := time.Now() + count := 0 + + cvl.Initialize() + cv, _ := cvl.ValidationSessOpen() + + if ((len(os.Args) > 1) && (os.Args[1] == "debug")) { + cvl.Debug(true) + } + + { + count++ + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "ACL_TABLE|TestACL1", + map[string]string { + "stage": "INGRESS", + "type": "L3", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "ACL_RULE|TestACL1|Rule1", + map[string]string { + "PACKET_ACTION": "FORWARD", + "SRC_IP": "10.1.1.1/32", + "L4_SRC_PORT": "1909", + "IP_PROTOCOL": "103", + "DST_IP": "20.2.2.2/32", + "L4_DST_PORT_RANGE": "9000-12000", + }, + }, + } + + fmt.Printf("\n\n%d. Validating create data = %v\n\n", count, cfgData); + + _, err := cv.ValidateEditConfig(cfgData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_TABLE|MyACL11_ACL_IPV4", + map[string]string { + "stage": "INGRESS", + "type": "MIRROR", + }, + }, + } + + fmt.Printf("\n\n%d. Validating update data = %v\n\n", count, cfgData); + + _, err := cv.ValidateEditConfig(cfgData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "MIRROR_SESSION|everflow", + map[string]string { + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2", + }, + }, + } + + fmt.Printf("\n\n%d. Validating create data = %v\n\n", count, cfgData); + _, err := cv.ValidateEditConfig(cfgData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + + count++ + cfgData = []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "MIRROR_SESSION|everflow", + map[string]string { + "src_ip": "10.1.0.32", + "dst_ip": "2.2.2.2", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_UPDATE, + "ACL_RULE|MyACL11_ACL_IPV4|RULE_1", + map[string]string { + "MIRROR_ACTION": "everflow", + }, + }, + } + + fmt.Printf("\n\n%d. Validating data for update = %v\n\n", count, cfgData); + + _, err = cv.ValidateEditConfig(cfgData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + + cfgData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "MIRROR_SESSION|everflow", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_RULE|MyACL11_ACL_IPV4|RULE_1", + map[string]string { + }, + }, + } + + fmt.Printf("\n\n%d. Validating data for delete = %v\n\n", count, cfgData); + + _, err := cv.ValidateEditConfig(cfgData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + + cvl.ValidationSessClose(cv) + cvl.Finish() + + fmt.Printf("\n\n\n Time taken for %v requests = %v\n", count, time.Since(start)) +} diff --git a/src/cvl/tests/cv_vlan.go b/src/cvl/tests/cv_vlan.go new file mode 100644 index 0000000000..b230e86b5a --- /dev/null +++ b/src/cvl/tests/cv_vlan.go @@ -0,0 +1,448 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "os" + "time" + "cvl" + "github.com/go-redis/redis" + "strconv" +) + +func getConfigDbClient() *redis.Client { + rclient := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + Password: "", // no password set + DB: 4, + DialTimeout: 0, + }) + _, err := rclient.Ping().Result() + if err != nil { + fmt.Printf("failed to connect to redis server %v", err) + } + return rclient +} + +/* Unloads the Config DB based on JSON File. */ +func unloadConfigDB(rclient *redis.Client, key string, data map[string]string) { + _, err := rclient.Del(key).Result() + + if err != nil { + fmt.Printf("Failed to delete for key %s, data %v, err %v", key, data, err) + } + +} + +/* Loads the Config DB based on JSON File. */ +func loadConfigDB(rclient *redis.Client, key string, data map[string]string) { + + dataTmp := make(map[string]interface{}) + + for k, v := range data { + dataTmp[k] = v + } + + _, err := rclient.HMSet(key, dataTmp).Result() + + if err != nil { + fmt.Printf("Failed to add for key %s, data %v, err %v", key, data, err) + } + +} + +func main() { + start := time.Now() + count := 0 + + cvl.Initialize() + if ((len(os.Args) > 1) && (os.Args[1] == "debug")) { + cvl.Debug(true) + } + + rclient := getConfigDbClient() + + if ((len(os.Args) > 1) && (os.Args[1] == "add")) { + + //Add ACL + vlanNoStart, _ := strconv.Atoi(os.Args[2]) + vlanNoEnd, _ := strconv.Atoi(os.Args[3]) + for vlanNum:= vlanNoStart ;vlanNum <= vlanNoEnd; vlanNum++ { + cvSess, _ := cvl.ValidationSessOpen() + + cfgDataVlan := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + fmt.Sprintf("VLAN|Vlan%d", vlanNum), + map[string]string { + "vlanid": fmt.Sprintf("%d", vlanNum), + "members@": "Ethernet0,Ethernet4,Ethernet8,Ethernet12,Ethernet16,Ethernet20,Ethernet24,Ethernet28", + }, + }, + } + + _, ret := cvSess.ValidateEditConfig(cfgDataVlan) + + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + return + } + + cfgDataVlan[0].VType = cvl.VALIDATE_NONE + + for i:=0; i<7; i++ { + cfgDataVlan = append(cfgDataVlan, cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + fmt.Sprintf("VLAN_MEMBER|Vlan%d|Ethernet%d", vlanNum, i * 4), + map[string]string { + "tagging_mode" : "tagged", + }, + }) + + _, ret1 := cvSess.ValidateEditConfig(cfgDataVlan) + if (ret1 != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + return + } + + cfgDataVlan[1 + i].VType = cvl.VALIDATE_NONE + } + + //Write to DB + for _, cfgDataItem := range cfgDataVlan { + loadConfigDB(rclient, cfgDataItem.Key, cfgDataItem.Data) + } + + cvl.ValidationSessClose(cvSess) + } + + return + } else if ((len(os.Args) > 1) && (os.Args[1] == "del")) { + vlanNoStart, _ := strconv.Atoi(os.Args[2]) + vlanNoEnd, _ := strconv.Atoi(os.Args[3]) + for vlanNum:= vlanNoStart ;vlanNum <= vlanNoEnd; vlanNum++ { + cvSess,_ := cvl.ValidationSessOpen() + + //Delete ACL + + cfgDataVlan := []cvl.CVLEditConfigData{} + + //Create 7 ACL rules + for i:=0; i<7; i++ { + cfgDataVlan = append(cfgDataVlan, cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + fmt.Sprintf("VLAN_MEMBER|Vlan%d|Ethernet%d", vlanNum, i * 4), + map[string]string { + }, + }) + + _, ret := cvSess.ValidateEditConfig(cfgDataVlan) + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + //return + } + + cfgDataVlan[i].VType = cvl.VALIDATE_NONE + } + + cfgDataVlan = append(cfgDataVlan, cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + fmt.Sprintf("VLAN|Vlan%d", vlanNum), + map[string]string { + }, + }) + + _, ret := cvSess.ValidateEditConfig(cfgDataVlan) + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("Validation failure\n") + //return + } + + //Write to DB + for _, cfgDataItem := range cfgDataVlan { + unloadConfigDB(rclient, cfgDataItem.Key, cfgDataItem.Data) + } + + cvl.ValidationSessClose(cvSess) + } + return + } + cv, ret := cvl.ValidationSessOpen() + if (ret != cvl.CVL_SUCCESS) { + fmt.Printf("NewDB: Could not create CVL session") + return + } + + { + count++ + keyData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL|ch1", + map[string]string { + "admin_status": "up", + "mtu": "9100", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL|ch2", + map[string]string { + "admin_status": "up", + "mtu": "9100", + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch1|Ethernet4", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch1|Ethernet8", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch2|Ethernet12", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch2|Ethernet16", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_NONE, + cvl.OP_NONE, + "PORTCHANNEL_MEMBER|ch2|Ethernet20", + map[string]string { + }, + }, + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_CREATE, + "VLAN|Vlan1001", + map[string]string { + "vlanid": "1001", + "members@": "Ethernet24,ch1,Ethernet8", + }, + }, + } + + fmt.Printf("\nValidating data for must = %v\n\n", keyData); + + _, err := cv.ValidateEditConfig(keyData) + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + + } + + { + keyData := []cvl.CVLEditConfigData { + cvl.CVLEditConfigData { + cvl.VALIDATE_ALL, + cvl.OP_DELETE, + "ACL_TABLE|MyACL1_ACL_IPV4", + map[string]string { + "type": "L3", + }, + }, + } + + _, err := cv.ValidateEditConfig(keyData) + + fmt.Printf("\nValidating field delete...\n\n"); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + + } + + { + count++ + jsonData :=`{ + "VLAN": { + "Vlan100": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + } + } + }` + + + err := cv.ValidateConfig(jsonData) + + fmt.Printf("\nValidating data = %v\n\n", jsonData); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + + { + count++ + jsonData :=`{ + "VLAN": { + "Vln100": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + } + } + }` + + + err := cv.ValidateConfig(jsonData) + + fmt.Printf("\nValidating data for key syntax = %v\n\n", jsonData); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + + { + count++ + jsonData :=`{ + "VLAN": { + "Vlan4096": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + } + } + }` + + + err := cv.ValidateConfig(jsonData) + + fmt.Printf("\nValidating data for range check = %v\n\n", jsonData); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + jsonData :=`{ + "VLAN": { + "Vlan201": { + "members": [ + "Ethernet44", + "Ethernet64" + ], + "vlanid": "100" + } + } + }` + + + err := cv.ValidateConfig(jsonData) + + fmt.Printf("\nValidating data for internal dependency check = %v\n\n", jsonData); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + { + count++ + /*jsonData :=`{ + "VLAN": { + "Vlan100": { + "members": [ + "Ethernet44", + "Ethernet964" + ], + "vlanid": "100" + }, + "Vlan1200": { + "members": [ + "Ethernet64", + "Ethernet1008" + ], + "vlanid": "1200" + } + } + }`*/ + jsonData :=`{ + "VLAN": { + "Vlan4095": { + "vlanid": "4995" + } + } + }` + + err := cv.ValidateConfig(jsonData) + + fmt.Printf("\nValidating data for external dependency check = %v\n\n", jsonData); + + if (err == cvl.CVL_SUCCESS) { + fmt.Printf("\nConfig Validation succeeded.\n\n"); + } else { + fmt.Printf("\nConfig Validation failed.\n\n"); + } + } + + cvl.ValidationSessClose(cv) + + cvl.Finish() + + fmt.Printf("\n\n\n Time taken for %v requests = %v\n", count, time.Since(start)) +} diff --git a/src/cvl/tests/run_test.sh b/src/cvl/tests/run_test.sh new file mode 100755 index 0000000000..e786ac0144 --- /dev/null +++ b/src/cvl/tests/run_test.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +profiling="" +testcase="" +coverpkgs="-coverpkg=cvl,cvl/internal/util,cvl/internal/yparser" + +if [ "${BUILD}:" != ":" ] ; then + go test -v -c -gcflags="all=-N -l" +fi + +if [ "${TESTCASE}:" != ":" ] ; then + testcase="-run ${TESTCASE}" +fi + +if [ "${PROFILE}:" != ":" ] ; then + profiling="-bench=. -benchmem -cpuprofile profile.out" +fi + +#Run test and display report +if [ "${NOREPORT}:" != ":" ] ; then + go test -v -cover ${coverpkgs} ${testcase} +elif [ "${COVERAGE}:" != ":" ] ; then + go test -v -cover -coverprofile coverage.out ${coverpkgs} ${testcase} + go tool cover -html=coverage.out +else + go test -v -cover -json ${profiling} ${testcase} | tparse -smallscreen -all +fi + +#With profiling +#go test -v -cover -json -bench=. -benchmem -cpuprofile profile.out | tparse -smallscreen -all + diff --git a/src/rest/Makefile b/src/rest/Makefile new file mode 100644 index 0000000000..0eb6901f44 --- /dev/null +++ b/src/rest/Makefile @@ -0,0 +1,85 @@ +################################################################################ +# # +# 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)) +BUILD_DIR ?= $(TOPDIR)/build + +GO ?= go +GOPATH ?= $(ABS_TOPDIR)/gopkgs:$(ABS_TOPDIR):$(shell $(GO) env GOPATH) +GOROOT ?= $(shell $(GO) env GOROOT) + + +REST_BUILD_DIR := $(BUILD_DIR)/rest_server +REST_BIN := $(REST_BUILD_DIR)/main +REST_TEST_BIN := $(BUILD_DIR)/tests/rest/server.test + +ALL_GO_SRCS = $(shell find $(TOPDIR)/src -name '*.go' | grep -v '_test.go' | grep -v '/tests\?/') +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) + +# Certificate generator tool for generating temp certificates. +# Compiled from standard crypto/tls/generate_cert.go +CERTGEN_BIN := $(REST_BUILD_DIR)/generate_cert + + +# Default target +all: $(REST_BIN) $(CERTGEN_BIN) $(REST_TEST_BIN) + +$(REST_BUILD_DIR)/: + mkdir -p $@ + +# REST Server binary +# 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 +else + GOPATH=$(REST_GOPATH) $(GO) build -gcflags="all=-N -l" -v -o $@ main/main.go +endif + +# Gotest binary for REST Server +$(REST_TEST_BIN): $(REST_TEST_SRCS) $(ALL_GO_SRCS) | $(REST_BUILD_DIR)/ + GOPATH=$(REST_GOPATH) $(GO) test -cover -c rest/server -o $@ + +# Compile a certificate generator temporary certificates from +# standard crypto/tls/generate_cert.go file. Source file will be +# available in GOROOT/src. +$(CERTGEN_BIN): | $(REST_BUILD_DIR)/ + $(GO) build -o $@ $(GOROOT)/src/crypto/tls/generate_cert.go + +test: + GOPATH=$(REST_GOPATH) $(GO) test -cover -v rest/server + +clean: + $(MAKE) -C $(TOPDIR)/models clean + $(MAKE) -C $(TOPDIR)/models/yang clean + rm -f $(REST_BIN) $(CERTGEN_BIN) + rm -f $(REST_TEST_BIN) + diff --git a/src/rest/main/main.go b/src/rest/main/main.go new file mode 100644 index 0000000000..7783ed1818 --- /dev/null +++ b/src/rest/main/main.go @@ -0,0 +1,219 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "crypto/tls" + "crypto/x509" + "flag" + "fmt" + "io/ioutil" + "net/http" + "os" + "os/signal" + "rest/server" + "swagger" + "syscall" + "time" + "github.com/golang/glog" + "github.com/pkg/profile" +) + +// Command line parameters +var ( + port int // Server port + uiDir string // SwaggerUI directory + certFile string // Server certificate file path + keyFile string // Server private key file path + caFile string // Client CA certificate file path + clientAuth string // Client auth mode + jwtValInt uint64 // JWT Valid Interval + jwtRefInt uint64 // JWT Refresh seconds before expiry +) + +func init() { + // Parse command line + flag.IntVar(&port, "port", 443, "Listen port") + flag.StringVar(&uiDir, "ui", "/rest_ui", "UI directory") + flag.StringVar(&certFile, "cert", "", "Server certificate file path") + flag.StringVar(&keyFile, "key", "", "Server private key file path") + flag.StringVar(&caFile, "cacert", "", "CA certificate for client certificate validation") + flag.StringVar(&clientAuth, "client_auth", "none", "Client auth mode - none|cert|user|jwt") + flag.Uint64Var(&jwtRefInt, "jwt_refresh_int", 30, "Seconds before JWT expiry the token can be refreshed.") + flag.Uint64Var(&jwtValInt, "jwt_valid_int", 3600, "Seconds that JWT token is valid for.") + flag.Parse() + // Suppress warning messages related to logging before flag parse + flag.CommandLine.Parse([]string{}) +} + +// Start REST server +func main() { + + /* Enable profiling by default. Send SIGUSR1 signal to rest_server to + * stop profiling and save data to /tmp/profile/cpu.pprof file. + * Copy over the cpu.pprof file and rest_server to a Linux host and run + * any of the following commands to generate a report in needed format. + * go tool pprof --txt ./rest_server ./cpu.pprof > report.txt + * go tool pprof --pdf ./rest_server ./cpu.pprof > report.pdf + * Note: install graphviz to generate the graph on a pdf format + */ + prof := profile.Start() + defer prof.Stop() + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGUSR1) + go func() { + <-sigs + prof.Stop() + }() + + swagger.Load() + + server.SetUIDirectory(uiDir) + + server.JwtRefreshInt = time.Duration(jwtRefInt*uint64(time.Second)) + server.JwtValidInt = time.Duration(jwtValInt*uint64(time.Second)) + + if clientAuth == "user" { + server.UserAuth.User = true + } + if clientAuth == "jwt" { + server.UserAuth.Jwt = true + server.GenerateJwtSecretKey() + } + + router := server.NewRouter() + + address := fmt.Sprintf(":%d", port) + + // Prepare TLSConfig from the parameters + tlsConfig := tls.Config{ + ClientAuth: getTLSClientAuthType(), + Certificates: prepareServerCertificate(), + ClientCAs: prepareCACertificates(), + MinVersion: tls.VersionTLS12, + CurvePreferences: getPreferredCurveIDs(), + PreferServerCipherSuites: true, + CipherSuites: getPreferredCipherSuites(), + } + + // Prepare HTTPS server + restServer := &http.Server{ + Addr: address, + Handler: router, + TLSConfig: &tlsConfig, + } + + glog.Infof("Server started on %v", address) + glog.Infof("UI directory is %v", uiDir) + + // Start HTTPS server + glog.Fatal(restServer.ListenAndServeTLS("", "")) +} + +// prepareServerCertificate function parses --cert and --key parameter +// values. Both cert and private key PEM files are loaded into a +// tls.Certificate objects. Exits the process if files are not +// specified or not found or corrupted. +func prepareServerCertificate() []tls.Certificate { + if certFile == "" { + glog.Fatal("Server certificate file not specified") + } + + if keyFile == "" { + glog.Fatal("Server private key file not specified") + } + + glog.Infof("Server certificate file: %s", certFile) + glog.Infof("Server private key file: %s", keyFile) + + certificate, err := tls.LoadX509KeyPair(certFile, keyFile) + if err != nil { + glog.Fatal("Failed to load server cert/key -- ", err) + } + + return []tls.Certificate{certificate} +} + +// prepareCACertificates function parses --ca parameter, which is the +// path to CA certificate file. Loads file contents to a x509.CertPool +// object. Returns nil if file name is empty (not specified). Exists +// the process if file path is invalid or file is corrupted. +func prepareCACertificates() *x509.CertPool { + if caFile == "" { // no CA file.. + return nil + } + + glog.Infof("Client CA certificate file: %s", caFile) + + caCert, err := ioutil.ReadFile(caFile) + if err != nil { + glog.Fatal("Failed to load CA certificate file -- ", err) + } + + caPool := x509.NewCertPool() + ok := caPool.AppendCertsFromPEM(caCert) + if !ok { + glog.Fatal("Invalid CA certificate") + } + + return caPool +} + +// getTLSClientAuthType function parses the --client_auth parameter. +// Returns corresponding tls.ClientAuthType value. Exits the process +// if value is not valid ('none', 'cert' or 'auth') +func getTLSClientAuthType() tls.ClientAuthType { + switch clientAuth { + case "none": + return tls.RequestClientCert + case "user": + return tls.RequestClientCert + case "cert": + if caFile == "" { + glog.Fatal("--cacert option is mandatory when --client_auth is 'cert'") + } + return tls.RequireAndVerifyClientCert + case "jwt": + return tls.RequestClientCert + default: + glog.Fatalf("Invalid '--client_auth' value '%s'. "+ + "Expecting one of 'none', 'cert', 'user' or 'jwt'", clientAuth) + return tls.RequireAndVerifyClientCert // dummy + } +} + +func getPreferredCurveIDs() []tls.CurveID { + return []tls.CurveID{ + tls.CurveP521, + tls.CurveP384, + tls.CurveP256, + } +} + +func getPreferredCipherSuites() []uint16 { + return []uint16{ + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + } +} diff --git a/src/rest/main/main_test.go b/src/rest/main/main_test.go new file mode 100644 index 0000000000..50ee1010da --- /dev/null +++ b/src/rest/main/main_test.go @@ -0,0 +1,38 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "os" + "os/signal" + "syscall" + "testing" +) + +func TestMain(t *testing.T) { + go main() + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGUSR1) + fmt.Println("Listening on sig kill from TestMain") + <-sigs + fmt.Println("Returning from TestMain on sig kill") +} + diff --git a/src/rest/server/basicAuth.go b/src/rest/server/basicAuth.go new file mode 100644 index 0000000000..5b0d780cab --- /dev/null +++ b/src/rest/server/basicAuth.go @@ -0,0 +1,35 @@ + +package server + +import ( + "net/http" + "github.com/golang/glog" +) +func BasicAuthenAndAuthor(r *http.Request, rc *RequestContext) error { + + username, passwd, authOK := r.BasicAuth() + if authOK == false { + glog.Errorf("[%s] User info not present", rc.ID) + return httpError(http.StatusUnauthorized, "") + } + + glog.Infof("[%s] Received user=%s", rc.ID, username) + + auth_success, err := UserPwAuth(username, passwd) + if auth_success == false { + glog.Infof("[%s] Failed to authenticate; %v", rc.ID, err) + return httpError(http.StatusUnauthorized, "") + } + + + glog.Infof("[%s] Authentication passed. user=%s ", rc.ID, username) + + //Allow SET request only if user belong to admin group + if isWriteOperation(r) && IsAdminGroup(username) == false { + glog.Errorf("[%s] Not an admin; cannot allow %s", rc.ID, r.Method) + return httpError(http.StatusForbidden, "Not an admin user") + } + + glog.Infof("[%s] Authorization passed", rc.ID) + return nil +} \ No newline at end of file diff --git a/src/rest/server/context.go b/src/rest/server/context.go new file mode 100644 index 0000000000..21674c7f31 --- /dev/null +++ b/src/rest/server/context.go @@ -0,0 +1,212 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "context" + "fmt" + "mime" + "net/http" + "regexp" + "sync/atomic" +) + +// RequestContext holds metadata about REST request. +type RequestContext struct { + + // Unique reqiest id + ID string + + // Name represents the operationId from OpenAPI spec + Name string + + // "consumes" and "produces" data from OpenAPI spec + Consumes MediaTypes + Produces MediaTypes + + // Model holds pointer to the OpenAPI data model object for + // the body. When set, the request handler can validate the + // request payload by loading the body into this model object. + Model interface{} + + // PMap is the mapping of URI parameter names to actual yang + // leaf names. Yang xpaths can have duplicate parameter names, + // which is not supported by swagger and mux libraries. We + // work around it by assigning different parameter names in + // swagger and map them back to yang names while converting + // REST paths to TransLib paths. + PMap NameMap +} + +type contextkey int + +const requestContextKey contextkey = 0 + +// Request Id generator +var requestCounter uint64 + +// GetContext function returns the RequestContext object for a +// HTTP request. RequestContext is maintained as a context value of +// the request. Creates a new RequestContext object is not already +// available; in which case this function also creates a copy of +// the HTTP request object with new context. +func GetContext(r *http.Request) (*RequestContext, *http.Request) { + cv := r.Context().Value(requestContextKey) + if cv != nil { + return cv.(*RequestContext), r + } + + rc := new(RequestContext) + rc.ID = fmt.Sprintf("REST-%v", atomic.AddUint64(&requestCounter, 1)) + + r = r.WithContext(context.WithValue(r.Context(), requestContextKey, rc)) + return rc, r +} + +/////////// + +// MediaType represents the parsed media type value. Includes +// a MIME type string and optional parameters. +type MediaType struct { + Type string + Params map[string]string + + TypePrefix string + TypeSuffix string + TypeMiddle string +} + +// mediaTypeExpr is the regex to extract parts from media type string. +var mediaTypeExpr = regexp.MustCompile(`([^/]+)(?:/(?:([^+]+)\+)?(.+))?`) + +// Parse function parses a full media type value with parameters +// into this MediaType object. +func parseMediaType(value string) (*MediaType, error) { + if value == "" { + return nil, nil + } + + mtype, params, err := mime.ParseMediaType(value) + if err != nil { + return nil, err + } + + // Extract parts from the mime type + parts := mediaTypeExpr.FindStringSubmatch(mtype) + if parts[3] == "*" && parts[2] == "" { // for patterns like "text/*" + parts[2] = "*" + } + + return &MediaType{Type: mtype, Params: params, + TypePrefix: parts[1], TypeMiddle: parts[2], TypeSuffix: parts[3]}, nil +} + +// Format function returns the full media type string - including +// MIME type and parameters. +func (m *MediaType) Format() string { + return mime.FormatMediaType(m.Type, m.Params) +} + +// Matches verifies if this Mediatype matches the another MediaType. +func (m *MediaType) Matches(other *MediaType) bool { + return m.Type == other.Type || + (matchPart(m.TypePrefix, other.TypePrefix) && + matchPart(m.TypeMiddle, other.TypeMiddle) && + matchPart(m.TypeSuffix, other.TypeSuffix)) +} + +// isJSON function checks if this media type represents a json +// content. Uses the suffix part of media type string. +func (m *MediaType) isJSON() bool { + return m.TypeSuffix == "json" +} + +func matchPart(x, y string) bool { + return x == y || x == "*" || y == "*" +} + +////////// + +// MediaTypes is a collection of parsed media type values +type MediaTypes []MediaType + +// Add function parses and adds a media type to the MediaTypes +// object. Any parameters in the media type value are ignored. +func (m *MediaTypes) Add(mimeType string) error { + mtype, err := parseMediaType(mimeType) + if err == nil { + *m = append(*m, *mtype) + } + + return err +} + +// Contains function checks if a given media type value is +// present in the ContentTypes. Ignores the media type parameters. +func (m *MediaTypes) Contains(mimeType string) bool { + t, _, _ := mime.ParseMediaType(mimeType) + for _, entry := range *m { + if entry.Type == t { + return true + } + } + + return false +} + +// GetMatching returns registered full content type value +// matching a given hint. +func (m *MediaTypes) GetMatching(mimeType string) MediaTypes { + mtype, err := parseMediaType(mimeType) + if err != nil { + return nil // TODO return error + } + + var matchList MediaTypes + for _, entry := range *m { + if entry.Matches(mtype) { + matchList = append(matchList, entry) + } + } + + return matchList +} + +func (m MediaTypes) String() string { + types := make([]string, 0, len(m)) + for _, entry := range m { + types = append(types, entry.Type) + } + return fmt.Sprintf("%v", types) +} + +////////// + +// NameMap is a simple mapping of names (string to string) +type NameMap map[string]string + +// Get function returns the mapped name for a given name. +// Returns given name itself if no mapping exists. +func (m *NameMap) Get(name string) string { + if mappedName, ok := (*m)[name]; ok { + return mappedName + } + return name +} diff --git a/src/rest/server/context_test.go b/src/rest/server/context_test.go new file mode 100644 index 0000000000..1df7fc3857 --- /dev/null +++ b/src/rest/server/context_test.go @@ -0,0 +1,228 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "net/http" + "reflect" + "strings" + "testing" +) + +func init() { + fmt.Println("+++++ init context_test +++++") +} + +func TestGetContext(t *testing.T) { + r, err := http.NewRequest("GET", "/index.html", nil) + if err != nil { + t.Fatalf("Unexpected error; %v", err) + } + + idSufix := fmt.Sprintf("-%v", requestCounter+1) + + rc1, r := GetContext(r) + rc2, r := GetContext(r) + rc3, r := GetContext(r) + + if rc1 != rc2 || rc1 != rc3 { + t.Fatalf("Got duplicate contexts!!") + } + + if !strings.HasSuffix(rc1.ID, idSufix) { + t.Fatalf("Unexpected id '%s'; expected suffix '%s", rc1.ID, idSufix) + } +} + +func TestParseEmptyMtype(t *testing.T) { + m, e := parseMediaType("") + if m != nil || e != nil { + t.Errorf("Unexpected return values; m=%v, e=%v", m, e) + } +} + +func TestParseMtype(t *testing.T) { + t.Run("X/Y", testParseMtype("application/json", + "application/json", "application", "", "json", mkmap())) + + t.Run("X/Y+Z", testParseMtype("application/yang-data+json", + "application/yang-data+json", "application", "yang-data", "json", mkmap())) + + t.Run("X/Z; A=B", testParseMtype("application/xml; q=5", + "application/xml", "application", "", "xml", mkmap("q", "5"))) + + t.Run("X/Z; A=B; C=D", testParseMtype("application/xml; q=5; ver=0.1", + "application/xml", "application", "", "xml", mkmap("q", "5", "ver", "0.1"))) + + t.Run("*/*", testParseMtype("*/*", + "*/*", "*", "*", "*", mkmap())) + + t.Run("text/*", testParseMtype("text/*", + "text/*", "text", "*", "*", mkmap())) + + t.Run("*/xml", testParseMtype("*/xml", + "*/xml", "*", "", "xml", mkmap())) + + t.Run("text/*+xml", testParseMtype("text/*+xml", + "text/*+xml", "text", "*", "xml", mkmap())) + + // invalid media types + t.Run("Partial", testBadMtype("application/")) + t.Run("WithSpace", testBadMtype("app lication/json")) + t.Run("WithSpace2", testBadMtype("application/ json")) + t.Run("BadParam", testBadMtype("application/json;q=10 x=20")) +} + +func testParseMtype(v, mimeType, prefix, middle, suffix string, params map[string]string) func(*testing.T) { + return func(t *testing.T) { + m := toMtype(t, v) + if m.Type != mimeType || m.TypePrefix != prefix || + m.TypeMiddle != middle || m.TypeSuffix != suffix || + reflect.DeepEqual(m.Params, params) == false { + t.Fatalf("\"%s\" did not tokenize into mime=\"%s\", prefix=\"%s\", middle=\"%s\", suffix=\"%s\", params=%v", + v, mimeType, prefix, middle, suffix, params) + } + if m.Format() != v { + t.Logf("Could not reconstruct \"%s\" from %v", v, m) + } + } +} + +func testBadMtype(v string) func(*testing.T) { + return func(t *testing.T) { + _, e := parseMediaType(v) + if e == nil { + t.Errorf("Invalid media type \"%s\" not flagged", v) + } + } +} + +func toMtype(t *testing.T, v string) *MediaType { + m, e := parseMediaType(v) + if e != nil { + t.Fatalf("Bad media type \"%s\"; err=%v", v, e) + } + return m +} + +func mkmap(args ...string) map[string]string { + m := make(map[string]string) + for i := 0; i < len(args); i += 2 { + m[args[i]] = args[i+1] + } + return m +} + +func TestMtypeMatch(t *testing.T) { + t.Run("A/B=~A/B", testMtypeMatch("text/json", "text/json", true)) + t.Run("A/B!~A/C", testMtypeMatch("text/json", "text/xml", false)) + t.Run("A/B!~C/B", testMtypeMatch("text/json", "new/json", false)) + t.Run("A/B=~*/*", testMtypeMatch("text/json", "*/*", true)) + t.Run("A/B=~A/*", testMtypeMatch("text/json", "text/*", true)) + t.Run("A/B=~*/B", testMtypeMatch("text/json", "*/json", true)) + t.Run("A/B!~*/C+B", testMtypeMatch("text/json", "*/new+json", false)) + t.Run("A/B!~*/B+*", testMtypeMatch("text/json", "*/json+*", false)) + t.Run("A/B=~A/*+B", testMtypeMatch("text/json", "text/*+json", true)) + t.Run("A/B=~A/*+*", testMtypeMatch("text/json", "text/*+*", true)) + t.Run("A/V+B=~A/V+B", testMtypeMatch("text/v1+json", "text/v1+json", true)) + t.Run("A/V+B=~A/V+*", testMtypeMatch("text/v1+json", "text/v1+*", true)) + t.Run("A/V+B=~A/*+B", testMtypeMatch("text/v1+json", "text/*+json", true)) + t.Run("A/V+B=~A/*", testMtypeMatch("text/v1+json", "text/*", true)) + t.Run("A/V+B=~*/*", testMtypeMatch("text/v1+json", "*/*", true)) + t.Run("A/V+B!~A/B", testMtypeMatch("text/v1+json", "text/json", false)) +} + +func testMtypeMatch(lhs, rhs string, exp bool) func(*testing.T) { + return func(t *testing.T) { + x := toMtype(t, lhs) + y := toMtype(t, rhs) + if x.Matches(y) != exp { + t.Fatalf("condition failed: \"%s\" match \"%s\" == %v", lhs, rhs, exp) + } + } +} + +func TestIsJSON(t *testing.T) { + t.Run("A/json", testIsJSON("text/json", true)) + t.Run("A/V+json", testIsJSON("text/v1+json", true)) + t.Run("A/json+V", testIsJSON("text/json+V", false)) + t.Run("A/xml", testIsJSON("text/xml", false)) + t.Run("json/A", testIsJSON("json/text", false)) +} + +func testIsJSON(v string, exp bool) func(*testing.T) { + return func(t *testing.T) { + m := toMtype(t, v) + if m.isJSON() != exp { + t.Fatalf("condition failed: isJson(\"%s\") == %v", v, exp) + } + } +} + +func TestMtypes(t *testing.T) { + var m MediaTypes + + // add 3 values to MediaTypes + m.Add("text/xml; q=5") + m.Add("text/json; q=2; r=3") + m.Add("text/v1+json") + + // check length + if len(m) != 3 { + t.Fatalf("Expected 3 items; found %d", len(m)) + } + + // check String() + expStrValue := "[text/xml text/json text/v1+json]" + if m.String() != expStrValue { + t.Fatalf("String() check failed.. expected \"%s\"; found \"%s\"", expStrValue, m) + } + + // check Contains() + t.Run("Contains#1", testMtypesContains(m, "text/xml", true)) + t.Run("Contains#2", testMtypesContains(m, "text/json", true)) + t.Run("Contains#3", testMtypesContains(m, "text/v1+json", true)) + t.Run("NotContains", testMtypesContains(m, "text/plain", false)) + + // check GetMatching() + t.Run("Match 1", testMtypesGetMatching(m, "text/xml", "[text/xml]")) + t.Run("Match 2", testMtypesGetMatching(m, "text/*+json", "[text/json text/v1+json]")) + t.Run("Match 0", testMtypesGetMatching(m, "text/plain", "[]")) + t.Run("Match Err", testMtypesGetMatching(m, "text plain", "[]")) +} + +func testMtypesContains(m MediaTypes, v string, exp bool) func(*testing.T) { + return func(t *testing.T) { + if m.Contains(v) != exp { + t.Fatalf("condition failed: %v.Contains(\"%s\") == %v", m, v, exp) + } + } +} + +func testMtypesGetMatching(m MediaTypes, v, exp string) func(*testing.T) { + return func(t *testing.T) { + m1 := m.GetMatching(v) + if m1.String() != exp { + t.Logf("Items matching \"%s\" from %s are %s", v, m, m1) + t.Fatalf("Expected %s", exp) + } + } +} diff --git a/src/rest/server/error.go b/src/rest/server/error.go new file mode 100644 index 0000000000..0335e70eac --- /dev/null +++ b/src/rest/server/error.go @@ -0,0 +1,202 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "encoding/json" + "fmt" + "net/http" + + "cvl" + "translib/tlerr" +) + +// errorResponse defines the RESTCONF compliant error response +// payload. It includes a list of errorEntry object. +type errorResponse struct { + Err struct { + Arr []errorEntry `json:"error"` + } `json:"ietf-restconf:errors"` +} + +// errorEntry defines the RESTCONF compilant error information +// payload. +type errorEntry struct { + Type errtype `json:"error-type"` + Tag errtag `json:"error-tag"` + AppTag string `json:"error-app-tag,omitempty"` + Path string `json:"error-path,omitempty"` + Message string `json:"error-message,omitempty"` +} + +type errtype string +type errtag string + +const ( + // error-type values + errtypeProtocol errtype = "protocol" + errtypeApplication errtype = "application" + + // error-tag values + errtagInvalidValue errtag = "invalid-value" + errtagOperationFailed errtag = "operation-failed" + errtagOperationNotSupported errtag = "operation-not-supported" + errtagAccessDenied errtag = "access-denied" + errtagResourceDenied errtag = "resource-denied" + errtagInUse errtag = "in-use" + errtagMalformedMessage errtag = "malformed-message" +) + +// httpErrorType is an error structure for indicating HTTP protocol +// errors. Includes HTTP status code and user displayable message. +type httpErrorType struct { + status int + message string +} + +func (e httpErrorType) Error() string { + return e.message +} + +func httpError(status int, msg string, args ...interface{}) error { + return httpErrorType{ + status: status, + message: fmt.Sprintf(msg, args...)} +} + +func httpBadRequest(msg string, args ...interface{}) error { + return httpError(http.StatusBadRequest, msg, args...) +} + +func httpServerError(msg string, args ...interface{}) error { + return httpError(http.StatusInternalServerError, msg, args...) +} + +// prepareErrorResponse returns HTTP status code and response payload +// for an error object. Response payalod is formatted as per RESTCONF +// specification (RFC8040, section 7.1). Uses json encoding. +func prepareErrorResponse(err error, r *http.Request) (status int, data []byte, mimeType string) { + status, entry := toErrorEntry(err, r) + var resp errorResponse + resp.Err.Arr = append(resp.Err.Arr, entry) + data, _ = json.Marshal(&resp) + mimeType = "application/yang-data+json" + return +} + +// toErrorEntry translates an error object into HTTP status and an +// errorEntry object. +func toErrorEntry(err error, r *http.Request) (status int, errInfo errorEntry) { + // By default everything is 500 Internal Server Error + status = http.StatusInternalServerError + errInfo.Type = errtypeApplication + errInfo.Tag = errtagOperationFailed + + switch e := err.(type) { + case httpErrorType: + status = e.status + errInfo.Type = errtypeProtocol + errInfo.Message = e.message + + // Guess error app tag from http status code + switch status { + case http.StatusBadRequest: // 400 + errInfo.Tag = errtagInvalidValue + case http.StatusUnauthorized: // 401 + errInfo.Tag = errtagAccessDenied + case http.StatusForbidden: // 403 + errInfo.Tag = errtagAccessDenied + case http.StatusNotFound: // 404 + errInfo.Tag = errtagInvalidValue + case http.StatusMethodNotAllowed: // 405 + errInfo.Tag = errtagOperationNotSupported + case http.StatusUnsupportedMediaType: + errInfo.Tag = errtagInvalidValue + default: // 5xx and others + errInfo.Tag = errtagOperationFailed + } + + case tlerr.TranslibSyntaxValidationError: + status = http.StatusBadRequest + errInfo.Type = errtypeProtocol + errInfo.Tag = errtagInvalidValue + errInfo.Message = e.ErrorStr.Error() + + case tlerr.TranslibRedisClientEntryNotExist: + status = http.StatusNotFound + errInfo.Tag = errtagInvalidValue + errInfo.Message = "Entry not found" + + case tlerr.TranslibCVLFailure: + status = http.StatusInternalServerError + errInfo.Tag = errtagInvalidValue + errInfo.Message = e.CVLErrorInfo.ConstraintErrMsg + errInfo.AppTag = e.CVLErrorInfo.ErrAppTag + + switch cvl.CVLRetCode(e.Code) { + case cvl.CVL_SEMANTIC_KEY_ALREADY_EXIST, cvl.CVL_SEMANTIC_KEY_DUPLICATE: + status = http.StatusConflict + errInfo.Tag = errtagResourceDenied + errInfo.Message = "Entry already exists" + + case cvl.CVL_SEMANTIC_KEY_NOT_EXIST: + status = http.StatusNotFound + errInfo.Tag = errtagInvalidValue + errInfo.Message = "Entry not found" + } + + case tlerr.TranslibTransactionFail: + status = http.StatusConflict + errInfo.Type = errtypeProtocol + errInfo.Tag = errtagInUse + errInfo.Message = "Transaction failed. Please try again." + + case tlerr.InternalError: + errInfo.Message = e.Error() + errInfo.Path = e.Path + + case tlerr.NotSupportedError: + status = http.StatusMethodNotAllowed + errInfo.Tag = errtagOperationNotSupported + errInfo.Message = e.Error() + errInfo.Path = e.Path + + case tlerr.InvalidArgsError: + status = http.StatusBadRequest + errInfo.Tag = errtagInvalidValue + errInfo.Message = e.Error() + errInfo.Path = e.Path + + case tlerr.NotFoundError: + status = http.StatusNotFound + errInfo.Tag = errtagInvalidValue + errInfo.Message = e.Error() + errInfo.Path = e.Path + + case tlerr.AlreadyExistsError: + status = http.StatusConflict + errInfo.Tag = errtagResourceDenied + errInfo.Message = e.Error() + errInfo.Path = e.Path + + } + + return +} diff --git a/src/rest/server/error_test.go b/src/rest/server/error_test.go new file mode 100644 index 0000000000..7dfdfce3df --- /dev/null +++ b/src/rest/server/error_test.go @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "errors" + "fmt" + "strings" + "testing" + + "cvl" + "translib/tlerr" +) + +func init() { + fmt.Println("+++++ init error_test +++++") +} + +func TestProtoError(t *testing.T) { + t.Run("400", testProtoError( + httpBadRequest("Bad %s", "json"), 400, "Bad json")) + + t.Run("500", testProtoError( + httpServerError("Failed"), 500, "Failed")) + + t.Run("XXX", testProtoError( + httpError(401, "Invalid user"), 401, "Invalid user")) +} + +func testProtoError(err error, status int, msg string) func(*testing.T) { + return func(t *testing.T) { + e, ok := err.(httpErrorType) + if !ok { + t.Error("not a httpErrorType") + } else if e.status != status || chkmsg(e.message, msg) == false { + t.Errorf("expecting %d/'%s'; found %d/'%s'", + status, msg, e.status, e.message) + } + } +} + +func TestErrorEntry(t *testing.T) { + + // errorEntry mapping for server errors + + t.Run("RequestReadError", testErrorEntry( + httpBadRequest("hii"), + 400, "protocol", "invalid-value", "", "hii")) + + t.Run("GenericServerError", testErrorEntry( + httpServerError("hii"), + 500, "protocol", "operation-failed", "", "hii")) + + t.Run("AuthenticationFailed", testErrorEntry( + httpError(401, "hii"), + 401, "protocol", "access-denied", "", "hii")) + + t.Run("AuthorizationFailed", testErrorEntry( + httpError(403, "hii"), + 403, "protocol", "access-denied", "", "hii")) + + t.Run("NotFound", testErrorEntry( + httpError(404, "404 NotFound."), + 404, "protocol", "invalid-value", "", "404 NotFound.")) + + t.Run("NotSupported", testErrorEntry( + httpError(405, "405 NotSupported."), + 405, "protocol", "operation-not-supported", "", "405 NotSupported.")) + + t.Run("UnknownMediaType", testErrorEntry( + httpError(415, "hii"), + 415, "protocol", "invalid-value", "", "hii")) + + // errorEntry mapping for unknown errors + + t.Run("UnknownError", testErrorEntry( + errors.New("hii"), + 500, "application", "operation-failed", "", "")) + + // errorEntry mapping for app errors + + t.Run("InvalidArgs", testErrorEntry( + tlerr.InvalidArgsError{Format: "hii", Path: "xyz"}, + 400, "application", "invalid-value", "xyz", "hii")) + + t.Run("ResourceNotFound", testErrorEntry( + tlerr.NotFoundError{Format: "hii", Path: "xyz"}, + 404, "application", "invalid-value", "xyz", "hii")) + + t.Run("AlreadyExists", testErrorEntry( + tlerr.AlreadyExistsError{Format: "hii", Path: "xyz"}, + 409, "application", "resource-denied", "xyz", "hii")) + + t.Run("UnsupportedOper", testErrorEntry( + tlerr.NotSupportedError{Format: "hii", Path: "xyz"}, + 405, "application", "operation-not-supported", "xyz", "hii")) + + t.Run("AppGenericErr", testErrorEntry( + tlerr.InternalError{Format: "hii", Path: "xyz"}, + 500, "application", "operation-failed", "xyz", "hii")) + + // errorEntry mapping for DB errors + + t.Run("DB_EntryNotExist", testErrorEntry( + tlerr.TranslibRedisClientEntryNotExist{}, + 404, "application", "invalid-value", "", "Entry not found")) + + t.Run("TransactionFailed", testErrorEntry( + tlerr.TranslibTransactionFail{}, + 409, "protocol", "in-use", "", "*")) + + t.Run("DB_CannotOpen", testErrorEntry( + tlerr.TranslibDBCannotOpen{}, + 500, "application", "operation-failed", "", "")) + + t.Run("DB_NotInit", testErrorEntry( + tlerr.TranslibDBNotInit{}, + 500, "application", "operation-failed", "", "")) + + t.Run("DB_SubscribeFailed", testErrorEntry( + tlerr.TranslibDBSubscribeFail{}, + 500, "application", "operation-failed", "", "")) + + // errorEntry mapping for CVL errors + + t.Run("CVL_KeyNotExists", testErrorEntry( + cvlError(cvl.CVL_SEMANTIC_KEY_NOT_EXIST, "hii"), + 404, "application", "invalid-value", "", "Entry not found")) + + t.Run("CVL_KeyExists", testErrorEntry( + cvlError(cvl.CVL_SEMANTIC_KEY_ALREADY_EXIST, "hii"), + 409, "application", "resource-denied", "", "Entry already exists")) + + t.Run("CVL_KeyDup", testErrorEntry( + cvlError(cvl.CVL_SEMANTIC_KEY_DUPLICATE, "hii"), + 409, "application", "resource-denied", "", "Entry already exists")) + + t.Run("CVL_SemanticErr", testErrorEntry( + cvlError(cvl.CVL_SEMANTIC_ERROR, "hii"), + 500, "application", "invalid-value", "", "hii")) + + // errorEntry mapping for YGOT errors + t.Run("YGOT_400", testErrorEntry( + tlerr.TranslibSyntaxValidationError{ErrorStr: errors.New("ygot")}, + 400, "protocol", "invalid-value", "", "ygot")) + +} + +func testErrorEntry(err error, + expStatus int, expType, expTag, expPath, expMessage string) func(*testing.T) { + return func(t *testing.T) { + status, entry := toErrorEntry(err, nil) + if status != expStatus || string(entry.Type) != expType || + string(entry.Tag) != expTag || entry.Path != expPath || + chkmsg(entry.Message, expMessage) == false { + t.Errorf("%T: expecting %d/%s/%s/\"%s\"/\"%s\"; found %d/%s/%s/\"%s\"/\"%s\"", + err, expStatus, expType, expTag, expPath, expMessage, + status, entry.Type, entry.Tag, entry.Path, entry.Message) + } + } +} + +func TestErrorResponse(t *testing.T) { + t.Run("WithMsg", testErrorResponse( + tlerr.NotFoundError{Format: "hii", Path: "xyz"}, + 404, "{\"ietf-restconf:errors\":{\"error\":[{"+ + "\"error-type\":\"application\",\"error-tag\":\"invalid-value\","+ + "\"error-path\":\"xyz\",\"error-message\":\"hii\"}]}}")) + + t.Run("NoMsg", testErrorResponse( + errors.New("hii"), + 500, "{\"ietf-restconf:errors\":{\"error\":[{"+ + "\"error-type\":\"application\",\"error-tag\":\"operation-failed\"}]}}")) +} + +func testErrorResponse(err error, expStatus int, expData string) func(*testing.T) { + return func(t *testing.T) { + status, data, ctype := prepareErrorResponse(err, nil) + + if status != expStatus { + t.Errorf("FAIL: bad status %d; expected %d", status, expStatus) + } else if ctype != "application/yang-data+json" { + t.Errorf("FAIL: bad content-type '%s'", ctype) + } else if string(data) != expData { + t.Errorf("FAIL: bad data %s", data) + t.Errorf("expected %s", expData) + } + } +} + +func chkmsg(actual, expected string) bool { + if expected == "*" { + return true + } + if strings.HasPrefix(expected, "!") { + return actual != expected[1:] + } + return actual == expected +} + +func cvlError(code cvl.CVLRetCode, msg string) error { + return tlerr.TranslibCVLFailure{ + Code: int(code), + CVLErrorInfo: cvl.CVLErrorInfo{ + ErrCode: code, + TableName: "unknown", + CVLErrDetails: "blah blah blah", + Msg: "ignore me", + ConstraintErrMsg: msg, + }, + } +} diff --git a/src/rest/server/handler.go b/src/rest/server/handler.go new file mode 100644 index 0000000000..6936ecf62b --- /dev/null +++ b/src/rest/server/handler.go @@ -0,0 +1,303 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "bytes" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "strings" + "translib" + "github.com/golang/glog" + "github.com/gorilla/mux" +) + +// Process function is the common landing place for all REST requests. +// Swagger code-gen should be configured to invoke this function +// from all generated stub functions. +func Process(w http.ResponseWriter, r *http.Request) { + rc, r := GetContext(r) + reqID := rc.ID + path := r.URL.Path + + var status int + var data []byte + var rtype string + + glog.Infof("[%s] %s %s; content-len=%d", reqID, r.Method, path, r.ContentLength) + _, body, err := getRequestBody(r, rc) + if err != nil { + status, data, rtype = prepareErrorResponse(err, r) + goto write_resp + } + + path = getPathForTranslib(r) + glog.Infof("[%s] Translated path = %s", reqID, path) + + status, data, err = invokeTranslib(r, path, body) + if err != nil { + glog.Errorf("[%s] Translib error %T - %v", reqID, err, err) + status, data, rtype = prepareErrorResponse(err, r) + goto write_resp + } + + rtype, err = resolveResponseContentType(data, r, rc) + if err != nil { + glog.Errorf("[%s] Failed to resolve response content-type, err=%v", rc.ID, err) + status, data, rtype = prepareErrorResponse(err, r) + goto write_resp + } + +write_resp: + glog.Infof("[%s] Sending response %d, type=%s, data=%s", reqID, status, rtype, data) + + // Write http response.. Following strict order should be + // maintained to form proper response. + // 1. Set custom headers via w.Header().Set("N", "V") + // 2. Set status code via w.WriteHeader(code) + // 3. Finally, write response body via w.Write(bytes) + if len(data) != 0 { + w.Header().Set("Content-Type", rtype) + w.WriteHeader(status) + w.Write([]byte(data)) + } else { + // No data, status only + w.WriteHeader(status) + } +} + +// getRequestBody returns the validated request body +func getRequestBody(r *http.Request, rc *RequestContext) (*MediaType, []byte, error) { + if r.ContentLength == 0 { + glog.Infof("[%s] No body", rc.ID) + return nil, nil, nil + } + + // read body + body, err := ioutil.ReadAll(r.Body) + if err != nil { + glog.Errorf("[%s] Failed to read body; err=%v", rc.ID, err) + return nil, nil, httpError(http.StatusInternalServerError, "") + } + + // Parse content-type header value + ctype := r.Header.Get("Content-Type") + + // Guess the contet type if client did not provide it + if ctype == "" { + glog.Infof("[%s] Content-type not provided in request. Guessing it...", rc.ID) + ctype = http.DetectContentType(body) + } + + ct, err := parseMediaType(ctype) + if err != nil { + glog.Errorf("[%s] Bad content-type '%s'; err=%v", + rc.ID, r.Header.Get("Content-Type"), err) + return nil, nil, httpBadRequest("Bad content-type") + } + + // Check if content type is one of the acceptable types specified + // in "consumes" section in OpenAPI spec. + if !rc.Consumes.Contains(ct.Type) { + glog.Errorf("[%s] Content-type '%s' not supported. Valid types %v", rc.ID, ct.Type, rc.Consumes) + return nil, nil, httpError(http.StatusUnsupportedMediaType, "Unsupported content-type") + } + + // Do payload validation if model info is set in the context. + if rc.Model != nil { + body, err = RequestValidate(body, ct, rc) + if err != nil { + return nil, nil, err + } + } + + glog.Infof("[%s] Content-type=%s; data=%s", rc.ID, ctype, body) + return ct, body, nil +} + +// resolveResponseContentType +func resolveResponseContentType(data []byte, r *http.Request, rc *RequestContext) (string, error) { + if len(data) == 0 { + return "", nil + } + + // If OpenAPI spec has only one "produces" option, assume that + // app module will return that exact type data!! + if len(rc.Produces) == 1 { + return rc.Produces[0].Format(), nil + } + + //TODO validate against Accept header + + return http.DetectContentType(data), nil +} + +// getPathForTranslib converts REST URIs into GNMI paths +func getPathForTranslib(r *http.Request) string { + // Return the URL path if no variables in the template.. + vars := mux.Vars(r) + if len(vars) == 0 { + return trimRestconfPrefix(r.URL.Path) + } + + path, err := mux.CurrentRoute(r).GetPathTemplate() + if err != nil { + glog.Infof("No path template for this route") + return trimRestconfPrefix(r.URL.Path) + } + + // Path is a template.. Convert it into GNMI style path + // WARNING: does not handle duplicate key attribute names + // + // Template = /openconfig-acl:acl/acl-sets/acl-set={name},{type} + // REST style = /openconfig-acl:acl/acl-sets/acl-set=TEST,ACL_IPV4 + // GNMI style = /openconfig-acl:acl/acl-sets/acl-set[name=TEST][type=ACL_IPV4] + path = trimRestconfPrefix(path) + path = strings.Replace(path, "={", "{", -1) + path = strings.Replace(path, "},{", "}{", -1) + rc, _ := GetContext(r) + + for k, v := range vars { + v, err = url.PathUnescape(v) + if err != nil { + glog.Warningf("Failed to unescape path var \"%s\". err=%v", v, err) + v = vars[k] + } + + restStyle := fmt.Sprintf("{%v}", k) + gnmiStyle := fmt.Sprintf("[%v=%v]", rc.PMap.Get(k), escapeKeyValue(v)) + path = strings.Replace(path, restStyle, gnmiStyle, 1) + } + + return path +} + +// escapeKeyValue function escapes a path key's value as per gNMI path +// conventions -- prefixes '\' to ']' and '\' +func escapeKeyValue(val string) string { + val = strings.Replace(val, "\\", "\\\\", -1) + val = strings.Replace(val, "]", "\\]", -1) + + return val +} + +// trimRestconfPrefix removes "/restconf/data" prefix from the path. +func trimRestconfPrefix(path string) string { + pattern := "/restconf/data/" + k := strings.Index(path, pattern) + if k < 0 { + pattern = "/restconf/operations/" + k = strings.Index(path, pattern) + } + if k >= 0 { + path = path[k+len(pattern)-1:] + } + + return path +} + +// isOperationsRequest checks if a request is a RESTCONF operations +// request (rpc or action) +func isOperationsRequest(r *http.Request) bool { + k := strings.Index(r.URL.Path, "/restconf/operations/") + return k >= 0 + //FIXME URI pattern will not help identifying yang action APIs. + //Use swagger generated API name instead??? +} + +// getRequestID returns the request id encoded in the context +func getRequestID(r *http.Request) string { + rc, _ := GetContext(r) + return rc.ID +} + +// invokeTranslib calls appropriate TransLib API for the given HTTP +// method. Returns response status code and content. +func invokeTranslib(r *http.Request, path string, payload []byte) (int, []byte, error) { + var status = 400 + var content []byte + var err error + + switch r.Method { + case "GET": + req := translib.GetRequest{Path: path} + resp, err1 := translib.Get(req) + if err1 == nil { + status = 200 + content = []byte(resp.Payload) + } else { + err = err1 + } + + case "POST": + if isOperationsRequest(r) { + req := translib.ActionRequest{Path: path, Payload: payload} + res, err1 := translib.Action(req) + if err1 == nil { + status = 200 + content = res.Payload + } else { + err = err1 + } + } else { + status = 201 + req := translib.SetRequest{Path: path, Payload: payload} + _, err = translib.Create(req) + } + + case "PUT": + //TODO send 201 if PUT resulted in creation + status = 204 + req := translib.SetRequest{Path: path, Payload: payload} + _, err = translib.Replace(req) + + case "PATCH": + status = 204 + req := translib.SetRequest{Path: path, Payload: payload} + _, err = translib.Update(req) + + case "DELETE": + status = 204 + req := translib.SetRequest{Path: path} + _, err = translib.Delete(req) + + default: + glog.Errorf("[%s] Unknown method '%v'", getRequestID(r), r.Method) + err = httpBadRequest("Invalid method") + } + + return status, content, err +} + +// hostMetadataHandler function handles "GET /.well-known/host-meta" +// request as per RFC6415. RESTCONF specification requires this for +// advertising the RESTCONF root path ("/restconf" in our case). +func hostMetadataHandler(w http.ResponseWriter, r *http.Request) { + var data bytes.Buffer + data.WriteString("") + data.WriteString("") + data.WriteString("") + + w.Header().Set("Content-Type", "application/xrd+xml") + w.Write(data.Bytes()) +} + diff --git a/src/rest/server/handler_test.go b/src/rest/server/handler_test.go new file mode 100644 index 0000000000..c3f00570cd --- /dev/null +++ b/src/rest/server/handler_test.go @@ -0,0 +1,619 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "encoding/xml" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/gorilla/mux" +) + +func init() { + fmt.Println("+++++ init handler_test +++++") +} + +var testRouter *mux.Router + +// Basic mux.Router tests +func TestRoutes(t *testing.T) { + initCount := countRoutes(NewRouter()) + + // Add couple of test handlers + + AddRoute("one", "GET", "/test/1", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(1) + }) + + AddRoute("two", "GET", "/test/2", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(2) + }) + + SetUIDirectory("/tmp/ui") // !!? + testRouter = NewRouter() + newCount := countRoutes(testRouter) + + if newCount != initCount+2 { + t.Fatalf("Expected route count %d; found %d", initCount+2, newCount) + } + + // Try the test URLs and an unknown URL. The unknonw path + // should return 404 + t.Run("Get1", testGet("/test/1", 1)) + t.Run("Get2", testGet("/test/2", 2)) + t.Run("GetUnknown", testGet("/test/unknown", 404)) + t.Run("Meta", testGet("/.well-known/host-meta", 200)) + + // Try the test URLs with authentication enabled.. This should + // fail the requests with 401 error. Unknown path should still + // return 404. + UserAuth.User = true + testRouter = NewRouter() + t.Run("Get1_auth", testGet("/test/1", 401)) + t.Run("Get2_auth", testGet("/test/2", 401)) + t.Run("GetUnknown_auth", testGet("/test/unknown", 404)) + + // Meta handler should not be affected by user auth + t.Run("Meta_auth", testGet("/.well-known/host-meta", 200)) + + // Cleanup for next tests + UserAuth.User = false + testRouter = nil +} + +// countRoutes counts the registered routes in a mux.Router +// object by walking it +func countRoutes(r *mux.Router) int { + var count int + r.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + count++ + return nil + }) + + return count +} + +// Try the url and check response code +func testGet(url string, expStatus int) func(*testing.T) { + return func(t *testing.T) { + w := httptest.NewRecorder() + testRouter.ServeHTTP(w, httptest.NewRequest("GET", url, nil)) + if w.Code != expStatus { + t.Fatalf("Expected response code %d; found %d", expStatus, w.Code) + } + } +} + +func TestMetadataHandler(t *testing.T) { + r := httptest.NewRequest("GET", "/.well-known/host-meta", nil) + w := httptest.NewRecorder() + + NewRouter().ServeHTTP(w, r) + + if w.Code != 200 { + t.Fatalf("Request failed with status %d", w.Code) + } + + ct, _ := parseMediaType(w.Header().Get("content-type")) + if ct == nil || ct.Type != "application/xrd+xml" { + t.Fatalf("Unexpected content-type '%s'", w.Header().Get("content-type")) + } + + data := w.Body.Bytes() + if len(data) == 0 { + t.Fatalf("No response body") + } + + var payload struct { + XMLName xml.Name `xml:"XRD"` + Links []struct { + Rel string `xml:"rel,attr"` + Href string `xml:"href,attr"` + } `xml:"Link"` + } + + err := xml.Unmarshal(data, &payload) + if err != nil { + t.Fatalf("Response parsing failed; err=%v", err) + } + + if payload.XMLName.Local != "XRD" || + payload.XMLName.Space != "http://docs.oasis-open.org/ns/xri/xrd-1.0" { + t.Fatalf("Invalid response '%s'", data) + } + + var rcRoot string + for _, x := range payload.Links { + if x.Rel == "restconf" { + rcRoot = x.Href + } + } + + t.Logf("Restconf root = '%s'", rcRoot) + if rcRoot != "/restconf" { + t.Fatalf("Invalid restconf root; expected '/restconf'") + } +} + +// Test REST to Translib path conversions +func TestPathConv(t *testing.T) { + + t.Run("novar", testPathConv( + "/simple/url/with/no/vars", + "/simple/url/with/no/vars", + "/simple/url/with/no/vars")) + + t.Run("1var", testPathConv( + "/sample/id={name}", + "/sample/id=TEST1", + "/sample/id[name=TEST1]")) + + t.Run("1var_no=", testPathConv( + "/sample/{name}", + "/sample/TEST1", + "/sample/[name=TEST1]")) + + t.Run("1var_middle", testPathConv( + "/sample/id={name}/test/suffix", + "/sample/id=TEST1/test/suffix", + "/sample/id[name=TEST1]/test/suffix")) + + t.Run("2vars", testPathConv( + "/sample/id={name},{type}", + "/sample/id=TEST2,NEW", + "/sample/id[name=TEST2][type=NEW]")) + + t.Run("2vars_middle", testPathConv( + "/sample/id={name},{type}/hey", + "/sample/id=TEST2,NEW/hey", + "/sample/id[name=TEST2][type=NEW]/hey")) + + t.Run("5vars", testPathConv( + "/sample/key={name},{type},{subtype},{color},{ver}", + "/sample/key=TEST2,NEW,LATEST,RED,1.0", + "/sample/key[name=TEST2][type=NEW][subtype=LATEST][color=RED][ver=1.0]")) + + t.Run("5vars_no=", testPathConv( + "/sample/{name},{type},{subtype},{color},{ver}", + "/sample/TEST2,NEW,LATEST,RED,1.0", + "/sample/[name=TEST2][type=NEW][subtype=LATEST][color=RED][ver=1.0]")) + + t.Run("multi", testPathConv( + "/sample/id={name},{type},{subtype}/data/color={colorname},{rgb}/{ver}", + "/sample/id=TEST2,NEW,LATEST/data/color=RED,ff0000/1.0", + "/sample/id[name=TEST2][type=NEW][subtype=LATEST]/data/color[colorname=RED][rgb=ff0000]/[ver=1.0]")) + + t.Run("rcdata_novar", testPathConv( + "/restconf/data/no/vars", + "/restconf/data/no/vars", + "/no/vars")) + + t.Run("xrcdata_novar", testPathConv( + "/myroot/restconf/data/no/vars", + "/myroot/restconf/data/no/vars", + "/no/vars")) + + t.Run("rcdata_1var", testPathConv( + "/restconf/data/id={name}", + "/restconf/data/id=TEST1", + "/id[name=TEST1]")) + + t.Run("xrcdata_1var", testPathConv( + "/myroot/restconf/data/id={name}", + "/myroot/restconf/data/id=TEST1", + "/id[name=TEST1]")) + + t.Run("no_template", testPathConv( + "*", + "/test/id=NOTEMPLATE", + "/test/id=NOTEMPLATE")) + + t.Run("empty_params", testPathConv2( + map[string]string{}, + "/test/id={name}", + "/test/id=X", + "/test/id[name=X]")) + + t.Run("1param", testPathConv2( + map[string]string{"name1": "name"}, + "/test/id={name1}", + "/test/id=X", + "/test/id[name=X]")) + + t.Run("nparams", testPathConv2( + map[string]string{"name1": "name", "name2": "name"}, + "/test/id={name1}/data/ref={name2}", + "/test/id=X/data/ref=Y", + "/test/id[name=X]/data/ref[name=Y]")) + + t.Run("extra_params", testPathConv2( + map[string]string{"name1": "name", "name2": "name"}, + "/test/id={name1}", + "/test/id=X", + "/test/id[name=X]")) + + t.Run("escaped", testPathConv( + "/test/interface={name}/ip={addr}", + "/test/interface=Ethernet%200%2f1/ip=10.0.0.1%2f24", + "/test/interface[name=Ethernet 0/1]/ip[addr=10.0.0.1/24]")) + + t.Run("escaped2", testPathConv( + "/test/interface={name},{ip}", + "/test/interface=Eth0%2f1%5b2%5c%5d,1::1", + "/test/interface[name=Eth0/1[2\\\\\\]][ip=1::1]")) + + t.Run("escaped+param", testPathConv2( + map[string]string{"name1": "name"}, + "/test/interface={name1},{type}", + "/test/interface=Eth0%2f1:1,PHY", + "/test/interface[name=Eth0/1:1][type=PHY]")) + +} + +// test handler to invoke getPathForTranslib and write the conveted +// path into response. Conversion logic depends on context values +// managed by mux router. Hence should be called from a handler. +var pathConvHandler = func(w http.ResponseWriter, r *http.Request) { + // t, err := mux.CurrentRoute(r).GetPathTemplate() + // fmt.Printf("Patt : %v (err=%v)\n", t, err) + // fmt.Printf("Vars : %v\n", mux.Vars(r)) + + w.Write([]byte(getPathForTranslib(r))) +} + +func testPathConv(template, path, expPath string) func(*testing.T) { + return testPathConv2(nil, template, path, expPath) +} + +func testPathConv2(m map[string]string, template, path, expPath string) func(*testing.T) { + return func(t *testing.T) { + router := NewRouter() //mux.NewRouter() + if template == "*" { + t.Logf("No template...") + router.Methods("GET").HandlerFunc(pathConvHandler) + } else { + router.HandleFunc(template, pathConvHandler) + } + + r := httptest.NewRequest("GET", path, nil) + w := httptest.NewRecorder() + + if m != nil { + rc, r1 := GetContext(r) + rc.PMap = m + r = r1 + } + + router.ServeHTTP(w, r) + + convPath := w.Body.String() + if convPath != expPath { + t.Logf("Conversion for template '%s' failed", template) + t.Logf("Input path '%s'", path) + t.Logf("Converted '%s'", convPath) + t.Logf("Expected '%s'", expPath) + t.FailNow() + } + } +} + +type errReader string + +func (er errReader) Read(p []byte) (n int, err error) { + return 0, errors.New(string(er)) +} + +func TestReqData_NoBody(t *testing.T) { + r := httptest.NewRequest("GET", "/test", nil) + rc := &RequestContext{ID: t.Name()} + + ct, data, err := getRequestBody(r, rc) + if ct != nil || data != nil || err != nil { + t.Fatalf("Expected nil response; found ct=%v, data=%v, err=%v", ct, data, err) + } +} + +func TestReqData_ReadFailure(t *testing.T) { + r := httptest.NewRequest("PUT", "/test", errReader("e-r-r-o-r")) + rc := &RequestContext{ID: t.Name()} + + testReqError(t, r, rc, 500) +} + +func TestReqData_Unknown(t *testing.T) { + r := httptest.NewRequest("PUT", "/test", strings.NewReader("Hello, world!")) + rc := &RequestContext{ID: t.Name()} + + testReqError(t, r, rc, 415) +} + +func TestReqData_Unknown2(t *testing.T) { + r := httptest.NewRequest("PUT", "/test", strings.NewReader("Hello, world!")) + rc := &RequestContext{ID: t.Name()} + rc.Consumes.Add("text/html") + + testReqError(t, r, rc, 415) +} + +func TestReqData_BadMime(t *testing.T) { + r := httptest.NewRequest("PUT", "/test", strings.NewReader("Hello, world!")) + r.Header.Set("content-type", "b a d") + rc := &RequestContext{ID: t.Name()} + rc.Consumes.Add("b a d") + + testReqError(t, r, rc, 400) +} + +func TestReqData_Text(t *testing.T) { + r := httptest.NewRequest("PUT", "/test", strings.NewReader("Hello, world!")) + rc := &RequestContext{ID: t.Name()} + rc.Consumes.Add("text/plain") + + testReqSuccess(t, r, rc, "text/plain", "Hello, world!") +} + +func TestReqData_Json(t *testing.T) { + input := "{\"one\":1}" + r := httptest.NewRequest("PUT", "/test", strings.NewReader(input)) + r.Header.Set("content-type", "application/json") + + rc := &RequestContext{ID: t.Name()} + rc.Consumes.Add("text/html") + rc.Consumes.Add("text/plain") + rc.Consumes.Add("application/json") + + testReqSuccess(t, r, rc, "application/json", input) +} + +func TestReqData_BadJsonNoValidation(t *testing.T) { + input := "{\"one:1}" + r := httptest.NewRequest("PUT", "/test", strings.NewReader(input)) + r.Header.Set("content-type", "application/json") + + rc := &RequestContext{ID: t.Name()} + rc.Consumes.Add("application/json") + + testReqSuccess(t, r, rc, "application/json", input) +} + +func TestReqData_BadJsonWithValidation(t *testing.T) { + input := "{\"one:1}" + r := httptest.NewRequest("PUT", "/test", strings.NewReader(input)) + r.Header.Set("content-type", "application/json") + + model := make(map[string]int) + rc := &RequestContext{ID: t.Name(), Model: &model} + rc.Consumes.Add("application/json") + + testReqError(t, r, rc, 400) +} + +func testReqSuccess(t *testing.T, r *http.Request, rc *RequestContext, expType, expData string) { + ct, data, err := getRequestBody(r, rc) + + if ct == nil || ct.Type != expType { + t.Fatalf("Expected %s; found %s", expType, ct.Type) + } + if data == nil || string(data) != expData { + t.Fatalf("Expected data \"%s\"; found \"%s\"", expData, data) + } + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } +} + +func testReqError(t *testing.T, r *http.Request, rc *RequestContext, expCode int) { + ct, data, err := getRequestBody(r, rc) + + if ct != nil { + t.Fatalf("Expected nil content-type; found %s", ct.Type) + } + if data != nil { + t.Fatalf("Expected nil data; found \"%s\"", data) + } + + he, ok := err.(httpErrorType) + if !ok { + t.Fatalf("Expecting httpErrorType; got %T", err) + } + if he.status != expCode { + t.Fatalf("Expecting http status %d; got %d", expCode, he.status) + } +} + +func TestRespData_NoContent(t *testing.T) { + rc := &RequestContext{ID: t.Name()} + t.Run("nil", testRespData(nil, rc, nil, "")) + t.Run("empty", testRespData(nil, rc, []byte(""), "")) +} + +func TestRespData_NoProduces(t *testing.T) { + rc := &RequestContext{ID: t.Name()} + t.Run("txt", testRespData(nil, rc, []byte("Hello, world!"), "text/plain")) + t.Run("bin", testRespData(nil, rc, make([]byte, 5), "application/octet-stream")) +} + +func TestRespData_1Produces(t *testing.T) { + rc := &RequestContext{ID: t.Name()} + rc.Produces.Add("application/json") + + t.Run("jsn", testRespData(nil, rc, []byte("{}"), "application/json")) + t.Run("bin", testRespData(nil, rc, make([]byte, 5), "application/json")) +} + +func TestRespData_nProduces(t *testing.T) { + rc := &RequestContext{ID: t.Name()} + rc.Produces.Add("application/json") + rc.Produces.Add("application/xml") + rc.Produces.Add("text/plain") + + t.Run("jsn", testRespData(nil, rc, []byte("{}"), "text/plain")) + t.Run("bin", testRespData(nil, rc, make([]byte, 5), "application/octet-stream")) +} + +func testRespData(r *http.Request, rc *RequestContext, data []byte, expType string) func(*testing.T) { + return func(t *testing.T) { + if r == nil { + r = httptest.NewRequest("GET", "/get", nil) + } + + ctype, err := resolveResponseContentType(data, r, rc) + ct, err := parseMediaType(ctype) + + if (expType == "" && ctype != "") || (ct != nil && ct.Type != expType) { + t.Fatalf("Expected resp content-type \"%s\"; got \"%s\"", expType, ctype) + } + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + } +} + +func TestProcessGET(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "GET", "/api-tests:sample", "")) + verifyResponse(t, w, 200) +} + +func TestProcessGET_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "GET", "/api-tests:sample/error/not-found", "")) + verifyResponse(t, w, 404) +} + +func TestProcessPUT(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "PUT", "/api-tests:sample", "{}")) + verifyResponse(t, w, 204) +} + +func TestProcessPUT_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "PUT", "/api-tests:sample/error/not-supported", "{}")) + verifyResponse(t, w, 405) +} + +func TestProcessPOST(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "POST", "/api-tests:sample", "{}")) + verifyResponse(t, w, 201) +} + +func TestProcessPOST_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "POST", "/api-tests:sample/error/invalid-args", "{}")) + verifyResponse(t, w, 400) +} + +func TestProcessPATCH(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "PATCH", "/api-tests:sample", "{}")) + verifyResponse(t, w, 204) +} + +func TestProcessPATCH_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "PATCH", "/api-tests:sample/error/unknown", "{}")) + verifyResponse(t, w, 500) +} + +func TestProcessDELETE(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "DELETE", "/api-tests:sample", "")) + verifyResponse(t, w, 204) +} + +func TestProcessDELETE_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "DELETE", "/api-tests:sample/error/not-found", "")) + verifyResponse(t, w, 404) +} + +func TestProcessRPC(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "POST", "/restconf/operations/api-tests:my-echo", + "{\"/api-tests:input\":{\"message\":\"Hii\"}}")) + verifyResponse(t, w, 200) +} + +func TestProcessRPC_error(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "POST", "/restconf/operations/api-tests:my-echo", + "{\"api-tests:input\":{\"error-type\":\"not-supported\"}}")) + verifyResponse(t, w, 405) +} + +func TestProcessBadMethod(t *testing.T) { + w := httptest.NewRecorder() + Process(w, prepareRequest(t, "TEST", "/test", "{}")) + verifyResponse(t, w, 400) +} + +func TestProcessBadContent(t *testing.T) { + w := httptest.NewRecorder() + r := prepareRequest(t, "PUT", "/test", "{}") + r.Header.Set("content-type", "bad/content") + + Process(w, r) + verifyResponse(t, w, 415) +} + +func TestProcessReadError(t *testing.T) { + w := httptest.NewRecorder() + r := httptest.NewRequest("PUT", "/test", errReader("simulated error")) + r.Header.Set("content-type", "application/json") + + rc, r := GetContext(r) + rc.ID = t.Name() + rc.Consumes.Add("application/json") + + Process(w, r) + verifyResponse(t, w, 500) +} + +func prepareRequest(t *testing.T, method, path, data string) *http.Request { + if !strings.Contains(path, "/restconf/") { + path = "/restconf/data" + path + } + + r := httptest.NewRequest(method, path, strings.NewReader(data)) + rc, r := GetContext(r) + rc.ID = t.Name() + + if data != "" { + r.Header.Set("content-type", "application/json") + rc.Consumes.Add("application/json") + } else { + rc.Produces.Add("application/json") + } + + return r +} + +func verifyResponse(t *testing.T, w *httptest.ResponseRecorder, expCode int) { + if w.Code != expCode { + t.Fatalf("Expecting response status %d; got %d", expCode, w.Code) + } +} diff --git a/src/rest/server/jwtAuth.go b/src/rest/server/jwtAuth.go new file mode 100644 index 0000000000..bc36481694 --- /dev/null +++ b/src/rest/server/jwtAuth.go @@ -0,0 +1,155 @@ +package server + +import ( + "time" + "encoding/json" + jwt "github.com/dgrijalva/jwt-go" + "net/http" + "crypto/rand" + "github.com/golang/glog" + "strings" +) +var ( + JwtRefreshInt time.Duration + JwtValidInt time.Duration + hmacSampleSecret = make([]byte, 16) +) +type Credentials struct { + Password string `json:"password"` + Username string `json:"username"` +} + + +type Claims struct { + Username string `json:"username"` + jwt.StandardClaims +} + +type jwtToken struct { + Token string `json:"access_token"` + TokenType string `json:"token_type"` + ExpIn int64 `json:"expires_in"` +} + +func generateJWT(username string, expire_dt time.Time) string { + // Create a new token object, specifying signing method and the claims + // you would like it to contain. + claims := &Claims{ + Username: username, + StandardClaims: jwt.StandardClaims{ + // In JWT, the expiry time is expressed as unix milliseconds + ExpiresAt: expire_dt.Unix(), + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Sign and get the complete encoded token as a string using the secret + tokenString, _ := token.SignedString(hmacSampleSecret) + + return tokenString +} +func GenerateJwtSecretKey() { + rand.Read(hmacSampleSecret) +} + +func tokenResp(w http.ResponseWriter, r *http.Request, username string) { + exp_tm := time.Now().Add(JwtValidInt) + token := jwtToken{Token: generateJWT(username, exp_tm), TokenType: "Bearer", ExpIn: int64(JwtValidInt/time.Second)} + resp,err := json.Marshal(token) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + status, data, ctype := prepareErrorResponse(httpError(http.StatusUnauthorized, err.Error()), r) + w.Header().Set("Content-Type", ctype) + w.WriteHeader(status) + w.Write(data) + return + } + w.Header().Set("Content-Type", "application/json") + w.Write(resp) +} + +func Authenticate(w http.ResponseWriter, r *http.Request) { + var creds Credentials + err := json.NewDecoder(r.Body).Decode(&creds) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + return + } + + auth_success, err := UserPwAuth(creds.Username, creds.Password) + if auth_success { + tokenResp(w, r, creds.Username) + return + + + } else { + status, data, ctype := prepareErrorResponse(httpError(http.StatusUnauthorized, ""), r) + w.Header().Set("Content-Type", ctype) + w.WriteHeader(status) + w.Write(data) + return + } +} + +func Refresh(w http.ResponseWriter, r *http.Request) { + + ctx,_ := GetContext(r) + token, err := JwtAuthenAndAuthor(r, ctx) + if err != nil { + status, data, ctype := prepareErrorResponse(httpError(http.StatusUnauthorized, ""), r) + w.Header().Set("Content-Type", ctype) + w.WriteHeader(status) + w.Write(data) + return + } + + claims := &Claims{} + jwt.ParseWithClaims(token.Token, claims, func(token *jwt.Token) (interface{}, error) { + return hmacSampleSecret, nil + }) + if time.Unix(claims.ExpiresAt, 0).Sub(time.Now()) > JwtRefreshInt { + status, data, ctype := prepareErrorResponse(httpError(http.StatusBadRequest, ""), r) + w.Header().Set("Content-Type", ctype) + w.WriteHeader(status) + w.Write(data) + return + } + tokenResp(w, r, claims.Username) + +} + +func JwtAuthenAndAuthor(r *http.Request, rc *RequestContext) (jwtToken, error) { + var token jwtToken + auth_hdr := r.Header.Get("Authorization") + if len(auth_hdr) == 0 { + glog.Errorf("[%s] JWT Token not present", rc.ID) + return token, httpError(http.StatusUnauthorized, "JWT Token not present") + } + auth_parts := strings.Split(auth_hdr, " ") + if len(auth_parts) != 2 || auth_parts[0] != "Bearer" { + glog.Errorf("[%s] Bad Request", rc.ID) + return token, httpError(http.StatusBadRequest, "Bad Request") + } + + token.Token = auth_parts[1] + + claims := &Claims{} + tkn, err := jwt.ParseWithClaims(token.Token, claims, func(token *jwt.Token) (interface{}, error) { + return hmacSampleSecret, nil + }) + if err != nil { + if err == jwt.ErrSignatureInvalid { + glog.Errorf("[%s] Failed to authenticate, Invalid JWT Signature", rc.ID) + return token, httpError(http.StatusUnauthorized, "Invalid JWT Signature") + + } + glog.Errorf("[%s] Bad Request", rc.ID) + return token, httpError(http.StatusBadRequest, "Bad Request") + } + if !tkn.Valid { + glog.Errorf("[%s] Failed to authenticate, Invalid JWT Token", rc.ID) + return token, httpError(http.StatusUnauthorized, "Invalid JWT Token") + } + return token, nil +} + diff --git a/src/rest/server/pamAuth.go b/src/rest/server/pamAuth.go new file mode 100644 index 0000000000..14ee280b84 --- /dev/null +++ b/src/rest/server/pamAuth.go @@ -0,0 +1,127 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "net/http" + "os/user" + "github.com/golang/glog" + //"github.com/msteinert/pam" + "golang.org/x/crypto/ssh" +) + +/* +type UserCredential struct { + Username string + Password string +} + +//PAM conversation handler. +func (u UserCredential) PAMConvHandler(s pam.Style, msg string) (string, error) { + + switch s { + case pam.PromptEchoOff: + return u.Password, nil + case pam.PromptEchoOn: + return u.Password, nil + case pam.ErrorMsg: + return "", nil + case pam.TextInfo: + return "", nil + default: + return "", errors.New("unrecognized conversation message style") + } +} + +// PAMAuthenticate performs PAM authentication for the user credentials provided +func (u UserCredential) PAMAuthenticate() error { + tx, err := pam.StartFunc("login", u.Username, u.PAMConvHandler) + if err != nil { + return err + } + return tx.Authenticate(0) +} + +func PAMAuthUser(u string, p string) error { + + cred := UserCredential{u, p} + err := cred.PAMAuthenticate() + return err +} +*/ + + +func IsAdminGroup(username string) bool { + + usr, err := user.Lookup(username) + if err != nil { + return false + } + gids, err := usr.GroupIds() + if err != nil { + return false + } + glog.V(2).Infof("User:%s, groups=%s", username, gids) + admin, err := user.Lookup("admin") + if err != nil { + return false + } + for _, x := range gids { + if x == admin.Gid { + return true + } + } + return false +} +func UserPwAuth(username string, passwd string) (bool, error) { + /* + * mgmt-framework container does not have access to /etc/passwd, /etc/group, + * /etc/shadow and /etc/tacplus_conf files of host. One option is to share + * /etc of host with /etc of container. For now disable this and use ssh + * for authentication. + */ + /* err := PAMAuthUser(username, passwd) + if err != nil { + log.Printf("Authentication failed. user=%s, error:%s", username, err.Error()) + return err + }*/ + + //Use ssh for authentication. + config := &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.Password(passwd), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } + _, err := ssh.Dial("tcp", "127.0.0.1:22", config) + if err != nil { + return false, err + } + + return true, nil +} + +// isWriteOperation checks if the HTTP request is a write operation +func isWriteOperation(r *http.Request) bool { + m := r.Method + return m == "POST" || m == "PUT" || m == "PATCH" || m == "DELETE" +} + diff --git a/src/rest/server/pamAuth_test.go b/src/rest/server/pamAuth_test.go new file mode 100644 index 0000000000..d3015ea09d --- /dev/null +++ b/src/rest/server/pamAuth_test.go @@ -0,0 +1,195 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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. +// +// Runs various combinations with local and TACACS+ user credentials. +// Test users should be already configured in the system. Below table +// lists various default user name and passwords and corresponding +// command line parameters to override them. +// +// Test user type User name Password Command line param +// ---------------------- ------------- ---------- ------------------- +// Local admin user testadmin password -ladmname -ladmpass +// Local non-admin user testuser password -lusrname -lusrpass +// TACACS+ admin user tactestadmin password -tadmname -tadmpass +// TACACS+ non-admin user tactestuser password -tusrname -tusrpass +// +// By default all test cases are skipped!! This is to avoid seeing test +// failures if target system is not ready. Command line param -authtest +// should be passed to enable the test cases. Valid values are "local" +// or "tacacs" or comma separated list of them. +// +// -authtest=local ==> Tests with only local user credentials +// -authtest=tacacs ==> Tests with only TACACS+ user credentials +// -authtest=local,tacacs ==> Tests with both local and TACACS+ users +// +/////////////////////////////////////////////////////////////////////// + +package server + +import ( + "flag" + "fmt" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" +) + +var authTest map[string]bool +var lusrName = flag.String("lusrname", "testuser", "Local non-admin username") +var lusrPass = flag.String("lusrpass", "password", "Local non-admin password") +var ladmName = flag.String("ladmname", "testadmin", "Local admin username") +var ladmPass = flag.String("ladmpass", "password", "Local admin password") +var tusrName = flag.String("tusrname", "tactestuser", "TACACS+ non-admin username") +var tusrPass = flag.String("tusrpass", "password", "TACACS+ non-admin password") +var tadmName = flag.String("tadmname", "tactestadmin", "TACACS+ admin username") +var tadmPass = flag.String("tadmpass", "password", "TACACS+ admin password") + +func init() { + fmt.Println("+++++ pamAuth_test +++++") +} + +func TestMain(m *testing.M) { + + t := flag.String("authtest", "", "Comma separated auth types to test (local tacacs)") + flag.Parse() + + var tlist []string + if *t != "" { + authTest = make(map[string]bool) + for _, x := range strings.Split(*t, ",") { + v := strings.ToLower(strings.TrimSpace(x)) + if v == "local" || v == "tacacs" { + authTest[v] = true + tlist = append(tlist, v) + } + } + if len(authTest) != 0 { + authTest[""] = true // Special key for any auth + } + } + + fmt.Println("+++++ Enabled auth test types", tlist) + + os.Exit(m.Run()) +} + +// Dummy test handler which returns 200 on success; 401 on +// authentication failure and 403 on authorization failure +var authTestHandler = authMiddleware(http.HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(200) + })) + +func TestLocalUser_Get(t *testing.T) { + ensureAuthTestEnabled(t, "local") + testAuthGet(t, *lusrName, *lusrPass, 200) +} + +func TestLocalUser_Set(t *testing.T) { + ensureAuthTestEnabled(t, "local") + testAuthSet(t, *lusrName, *lusrPass, 403) +} + +func TestLocalAdmin_Get(t *testing.T) { + ensureAuthTestEnabled(t, "local") + testAuthGet(t, *ladmName, *ladmPass, 200) +} + +func TestLocalAdmin_Set(t *testing.T) { + ensureAuthTestEnabled(t, "local") + testAuthSet(t, *ladmName, *ladmPass, 200) +} + +func TestTacacsUser_Get(t *testing.T) { + ensureAuthTestEnabled(t, "tacacs") + testAuthGet(t, *tusrName, *tusrPass, 200) +} + +func TestTacacslUser_Set(t *testing.T) { + ensureAuthTestEnabled(t, "tacacs") + testAuthSet(t, *tusrName, *tusrPass, 403) +} + +func TestTacacsAdmin_Get(t *testing.T) { + ensureAuthTestEnabled(t, "tacacs") + testAuthGet(t, *tadmName, *tadmPass, 200) +} + +func TestTacacsAdmin_Set(t *testing.T) { + ensureAuthTestEnabled(t, "tacacs") + testAuthSet(t, *tadmName, *tadmPass, 200) +} + +func TestAuth_NoUser(t *testing.T) { + ensureAuthTestEnabled(t, "") + testAuthGet(t, "", "", 401) + testAuthSet(t, "", "", 401) +} + +func TestAuth_BadUser(t *testing.T) { + ensureAuthTestEnabled(t, "") + testAuthGet(t, "baduserbaduserbaduser", "password", 401) + testAuthSet(t, "baduserbaduserbaduser", "password", 401) +} + +func TestAuth_BadPass(t *testing.T) { + ensureAuthTestEnabled(t, "") + testAuthGet(t, *lusrName, "Hello,world!", 401) + testAuthSet(t, *ladmName, "Hello,world!", 401) +} + +func ensureAuthTestEnabled(t *testing.T, authtype string) { + if _, ok := authTest[authtype]; !ok { + t.Skipf("%s auth tests not enabled.. Rerun with -authtest flag", authtype) + } +} + +func testAuthGet(t *testing.T, username, password string, expStatus int) { + t.Run("GET", testAuth("GET", username, password, expStatus)) + t.Run("HEAD", testAuth("HEAD", username, password, expStatus)) + t.Run("OPTIONS", testAuth("OPTIONS", username, password, expStatus)) +} + +func testAuthSet(t *testing.T, username, password string, expStatus int) { + t.Run("PUT", testAuth("PUT", username, password, expStatus)) + t.Run("POST", testAuth("POST", username, password, expStatus)) + t.Run("PATCH", testAuth("PATCH", username, password, expStatus)) + t.Run("DELETE", testAuth("DELETE", username, password, expStatus)) +} + +func testAuth(method, username, password string, expStatus int) func(*testing.T) { + return func(t *testing.T) { + r := httptest.NewRequest(method, "/auth", nil) + w := httptest.NewRecorder() + + if username != "" { + r.SetBasicAuth(username, password) + } + + authTestHandler.ServeHTTP(w, r) + + if w.Code != expStatus { + t.Fatalf("Expected response %d; got %d", expStatus, w.Code) + } + } +} diff --git a/src/rest/server/req_validate.go b/src/rest/server/req_validate.go new file mode 100644 index 0000000000..4571d8a100 --- /dev/null +++ b/src/rest/server/req_validate.go @@ -0,0 +1,94 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "encoding/json" + "reflect" + + "github.com/golang/glog" + "gopkg.in/go-playground/validator.v9" +) + +func isSkipValidation(t reflect.Type) bool { + if t == reflect.TypeOf([]int32{}) { + return true + } + + return false +} + +// RequestValidate performas payload validation of request body. +func RequestValidate(payload []byte, ctype *MediaType, rc *RequestContext) ([]byte, error) { + if ctype.isJSON() { + return validateRequestJSON(payload, rc) + } + + glog.Infof("[%s] Skipping payload validation for content-type '%v'", rc.ID, ctype.Type) + return payload, nil +} + +// validateRequestJSON performs payload validation for JSON data +func validateRequestJSON(jsn []byte, rc *RequestContext) ([]byte, error) { + var err error + v := rc.Model + glog.Infof("[%s] Unmarshalling %d bytes into %T", rc.ID, len(jsn), v) + + err = json.Unmarshal(jsn, v) + if err != nil { + glog.Errorf("[%s] json decoding error; %v", rc.ID, err) + return nil, httpBadRequest("Invalid json") + } + + //log.Printf("Received data: %s\n", jsn) + //log.Printf("Type is: %T, Value is:%v\n", v, v) + val := reflect.ValueOf(v) + if val.Kind() == reflect.Ptr && !val.IsNil() { + val = val.Elem() + } + + if !isSkipValidation(val.Type()) { + glog.Infof("[%s] Going to validate request", rc.ID) + validate := validator.New() + if val.Kind() == reflect.Slice { + //log.Println("Validate using Var") + err = validate.Var(v, "dive") + } else { + //log.Println("Validate using Struct") + err = validate.Struct(v) + } + if err != nil { + glog.Errorf("[%s] validation failed: %v", rc.ID, err) + return nil, httpBadRequest("Content not as per schema") + } + } else { + glog.Infof("[%s] Skipping payload validation for dataType %v", rc.ID, val.Type()) + } + + // Get sanitized json by marshalling validated body. Removes + // extra fields if any.. + newBody, err := json.Marshal(v) + if err != nil { + glog.Errorf("[%s] Failed to marshall; %v", rc.ID, err) + return nil, httpServerError("Internal error") + } + + return newBody, nil +} diff --git a/src/rest/server/router.go b/src/rest/server/router.go new file mode 100644 index 0000000000..f611897a23 --- /dev/null +++ b/src/rest/server/router.go @@ -0,0 +1,172 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "net/http" + "strings" + "time" + + "github.com/golang/glog" + "github.com/gorilla/mux" +) + +// Root directory for UI files +var swaggerUIDir = "./ui" +var UserAuth struct { + User bool + Cert bool + Jwt bool +} + +// SetUIDirectory functions sets directiry where Swagger UI +// resources are maintained. +func SetUIDirectory(directory string) { + swaggerUIDir = directory +} + + +// Route registration information +type Route struct { + Name string + Method string + Pattern string + Handler http.HandlerFunc +} + +// Collection of all routes +var allRoutes []Route + +// AddRoute appends specified routes to the routes collection. +// Called by init functions of swagger generated router.go files. +func AddRoute(name, method, pattern string, handler http.HandlerFunc) { + route := Route{ + Name: name, + Method: strings.ToUpper(method), + Pattern: pattern, + Handler: handler, + } + + allRoutes = append(allRoutes, route) +} + +// NewRouter function returns a new http router instance. Collects +// route information from swagger-codegen generated code and makes a +// github.com/gorilla/mux router object. +func NewRouter() *mux.Router { + router := mux.NewRouter().StrictSlash(true).UseEncodedPath() + + glog.Infof("Server has %d paths", len(allRoutes)) + + // Collect swagger generated route information + for _, route := range allRoutes { + handler := withMiddleware(route.Handler, route.Name) + + glog.V(2).Infof( + "Adding %s, %s %s", + route.Name, route.Method, route.Pattern) + + router. + Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(handler) + } + + // Documentation and test UI + uiHandler := http.StripPrefix("/ui/", http.FileServer(http.Dir(swaggerUIDir))) + router.Methods("GET").PathPrefix("/ui/").Handler(uiHandler) + + // Redirect "/ui" to "/ui/index.html" + router.Methods("GET").Path("/ui"). + Handler(http.RedirectHandler("/ui/index.html", 301)) + + //router.Methods("GET").Path("/model"). + // Handler(http.RedirectHandler("/ui/model.html", 301)) + + if UserAuth.Jwt { + router.Methods("POST").Path("/authenticate").Handler(http.HandlerFunc(Authenticate)) + router.Methods("POST").Path("/refresh").Handler(http.HandlerFunc(Refresh)) + + } + + + // Metadata discovery handler + metadataHandler := http.HandlerFunc(hostMetadataHandler) + router.Methods("GET").Path("/.well-known/host-meta"). + Handler(loggingMiddleware(metadataHandler, "hostMetadataHandler")) + + return router +} + +// loggingMiddleware returns a handler which times and logs the request. +// It should be the top handler in the middleware chain. +func loggingMiddleware(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rc, r := GetContext(r) + rc.Name = name + + glog.Infof("[%s] Recevied %s request from %s", rc.ID, name, r.RemoteAddr) + + start := time.Now() + + inner.ServeHTTP(w, r) + + glog.Infof("[%s] %s took %s", rc.ID, name, time.Since(start)) + }) +} + +// withMiddleware function prepares the default middleware chain for +// REST APIs. +func withMiddleware(h http.Handler, name string) http.Handler { + if UserAuth.User || UserAuth.Jwt { + h = authMiddleware(h) + } + + return loggingMiddleware(h, name) +} + +// authMiddleware function creates a middleware for request +// authentication and authorization. This middleware will return +// 401 response if authentication fails and 403 if authorization +// fails. +func authMiddleware(inner http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rc, r := GetContext(r) + var err error + if UserAuth.User { + err = BasicAuthenAndAuthor(r, rc) + + } + if UserAuth.Jwt { + _,err = JwtAuthenAndAuthor(r, rc) + } + + + if err != nil { + status, data, ctype := prepareErrorResponse(err, r) + w.Header().Set("Content-Type", ctype) + w.WriteHeader(status) + w.Write(data) + } else { + inner.ServeHTTP(w, r) + } + }) +} diff --git a/src/translib/Makefile b/src/translib/Makefile new file mode 100644 index 0000000000..69eec34521 --- /dev/null +++ b/src/translib/Makefile @@ -0,0 +1,60 @@ +####################################################################### +# +# Copyright 2019 Broadcom. All rights reserved. +# The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +# +####################################################################### + +ifeq ($(TOPDIR),) +TOPDIR := ../.. +endif + +ifeq ($(BUILD_DIR),) +BUILD_DIR := $(TOPDIR)/build +endif + +ifeq ($(GO),) +GO = /usr/local/go/bin/go +endif + +ifeq ($(GOPATH),) +GOPATH = $(shell $(GO) env GOPATH) +endif + +ifeq ($(GOROOT),) +GOROOT = $(shell $(GO) env GOROOT) +endif + +TRANSLIB_PKG = $(TOPDIR)/pkg/linux_amd64/translib.a + +TRANSLIB_MAIN_SRCS = $(shell find . -name '*.go' | grep -v '_test.go' | grep -v '/test/') +TRANSLIB_TEST_SRCS = $(shell find . -maxdepth 1 -name '*_test.go') +TRANSL_DB_ALL_SRCS = $(shell find db/ -name '*.go' | grep -v '/test/') + +TRANSLIB_TEST_DIR = $(BUILD_DIR)/tests/translib +TRANSLIB_TEST_BIN = $(TRANSLIB_TEST_DIR)/translib.test +TRANSL_DB_TEST_BIN = $(TRANSLIB_TEST_DIR)/db.test + +YANG_FILES = $(shell find $(TOPDIR)/models/yang -name '*.yang') +YGOT_BINDS = ocbinds/ocbinds.go + +all: $(TRANSLIB_PKG) $(TRANSLIB_TEST_BIN) $(TRANSL_DB_TEST_BIN) + +$(TRANSLIB_PKG): $(TRANSLIB_MAIN_SRCS) $(YGOT_BINDS) + $(GO) build -gcflags="all=-N -l" -v translib + $(GO) install translib + +$(TRANSLIB_TEST_BIN): $(TRANSLIB_MAIN_SRCS) $(TRANSLIB_TEST_SRCS) $(YGOT_BINDS) + $(GO) test -cover -coverpkg=translib,translib/tlerr -c translib -o $@ + +$(TRANSL_DB_TEST_BIN) : $(TRANSL_DB_ALL_SRCS) + $(GO) test -cover -c translib/db -o $@ + +$(YGOT_BINDS): $(YANG_FILES) + cd ocbinds && $(GO) generate + +clean: + rm -f $(YGOT_BINDS) + rm -f $(TRANSLIB_PKG) + rm -rf $(TRANSLIB_TEST_DIR) + diff --git a/src/translib/acl_app.go b/src/translib/acl_app.go new file mode 100644 index 0000000000..020cee5d2a --- /dev/null +++ b/src/translib/acl_app.go @@ -0,0 +1,1696 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "errors" + "bytes" + "fmt" + "reflect" + "strconv" + "strings" + "translib/db" + "translib/ocbinds" + "translib/tlerr" + + log "github.com/golang/glog" + "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygot" +) + +const ( + TABLE_SEPARATOR = "|" + KEY_SEPARATOR = "|" + ACL_TABLE = "ACL_TABLE" + RULE_TABLE = "ACL_RULE" + ACL_TYPE = "type" + ACL_DESCRIPTION = "policy_desc" + SONIC_ACL_TYPE_L2 = "L2" + SONIC_ACL_TYPE_IPV4 = "L3" + SONIC_ACL_TYPE_IPV6 = "L3V6" + OPENCONFIG_ACL_TYPE_IPV4 = "ACL_IPV4" + OPENCONFIG_ACL_TYPE_IPV6 = "ACL_IPV6" + OPENCONFIG_ACL_TYPE_L2 = "ACL_L2" + OC_ACL_APP_MODULE_NAME = "/openconfig-acl:acl" + OC_ACL_YANG_PATH_PREFIX = "/device/acl" + + MIN_PRIORITY = 1 + MAX_PRIORITY = 65536 +) + +var IP_PROTOCOL_MAP = map[ocbinds.E_OpenconfigPacketMatchTypes_IP_PROTOCOL]uint8{ + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_ICMP: 1, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_IGMP: 2, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_TCP: 6, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_UDP: 17, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_RSVP: 46, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_GRE: 47, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_AUTH: 51, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_PIM: 103, + ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_L2TP: 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, +} + +type AclApp struct { + pathInfo *PathInfo + ygotRoot *ygot.GoStruct + ygotTarget *interface{} + + aclTs *db.TableSpec + ruleTs *db.TableSpec + + aclTableMap map[string]db.Value + ruleTableMap map[string]map[string]db.Value +} + +func init() { + + err := register("/openconfig-acl:acl", + &appInfo{appType: reflect.TypeOf(AclApp{}), + ygotRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + isNative: false, + tablesToWatch: []*db.TableSpec{&db.TableSpec{Name: ACL_TABLE}, &db.TableSpec{Name: RULE_TABLE}}}) + + if err != nil { + log.Fatal("Register ACL app module with App Interface failed with error=", err) + } + + err = addModel(&ModelData{Name: "openconfig-acl", + Org: "OpenConfig working group", + Ver: "1.0.2"}) + if err != nil { + log.Fatal("Adding model data to appinterface failed with error=", err) + } +} + +func (app *AclApp) initialize(data appData) { + log.Info("initialize:acl:path =", data.path) + pathInfo := NewPathInfo(data.path) + *app = AclApp{pathInfo: pathInfo, ygotRoot: data.ygotRoot, ygotTarget: data.ygotTarget} + + app.aclTs = &db.TableSpec{Name: ACL_TABLE} + app.ruleTs = &db.TableSpec{Name: RULE_TABLE} + + app.aclTableMap = make(map[string]db.Value) + app.ruleTableMap = make(map[string]map[string]db.Value) +} + +func (app *AclApp) getAppRootObject() *ocbinds.OpenconfigAcl_Acl { + deviceObj := (*app.ygotRoot).(*ocbinds.Device) + return deviceObj.Acl +} + +func (app *AclApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCreate:acl:path =", app.pathInfo.Template) + + keys, err = app.translateCRUCommon(d, CREATE) + return keys, err +} + +func (app *AclApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateUpdate:acl:path =", app.pathInfo.Template) + + keys, err = app.translateCRUCommon(d, UPDATE) + return keys, err +} + +func (app *AclApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateReplace:acl:path =", app.pathInfo.Template) + + keys, err = app.translateCRUCommon(d, REPLACE) + return keys, err +} + +func (app *AclApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateDelete:acl:path =", app.pathInfo.Template) + + return keys, err +} + +func (app *AclApp) translateGet(dbs [db.MaxDB]*db.DB) error { + var err error + log.Info("translateGet:acl:path =", app.pathInfo.Template) + return err +} + +func (app *AclApp) translateAction(dbs [db.MaxDB]*db.DB) error { + err := errors.New("Not supported") + return err +} + +func (app *AclApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + pathInfo := NewPathInfo(path) + notifInfo := notificationInfo{dbno: db.ConfigDB} + notSupported := tlerr.NotSupportedError{ + Format: "Subscribe not supported", Path: path} + + if isSubtreeRequest(pathInfo.Template, "/openconfig-acl:acl/acl-sets") { + // Subscribing to top level ACL record is not supported. It requires listening + // to 2 tables (ACL and ACL_RULE); TransLib does not support it yet + if pathInfo.HasSuffix("/acl-sets") || + pathInfo.HasSuffix("/acl-set") || + pathInfo.HasSuffix("/acl-set{}{}") { + log.Errorf("Subscribe not supported for top level ACL %s", pathInfo.Template) + return nil, nil, notSupported + } + + t, err := getAclTypeOCEnumFromName(pathInfo.Var("type")) + if err != nil { + return nil, nil, err + } + + aclkey := getAclKeyStrFromOCKey(pathInfo.Var("name"), t) + + if strings.Contains(pathInfo.Template, "/acl-entry{}") { + // Subscribe for one rule + rulekey := "RULE_" + pathInfo.Var("sequence-id") + notifInfo.table = db.TableSpec{Name: RULE_TABLE} + notifInfo.key = asKey(aclkey, rulekey) + notifInfo.needCache = !pathInfo.HasSuffix("/acl-entry{}") + + } else if pathInfo.HasSuffix("/acl-entries") || pathInfo.HasSuffix("/acl-entry") { + // Subscribe for all rules of an ACL + notifInfo.table = db.TableSpec{Name: RULE_TABLE} + notifInfo.key = asKey(aclkey, "*") + + } else { + // Subscibe for ACL fields only + notifInfo.table = db.TableSpec{Name: ACL_TABLE} + notifInfo.key = asKey(aclkey) + notifInfo.needCache = true + } + + } else if isSubtreeRequest(pathInfo.Template, "/openconfig-acl:acl/interfaces") { + // Right now interface binding config is maintained within ACL + // table itself. Multiple ACLs can be bound to one intf; one + // inname can occur in multiple ACL entries. So we cannot map + // interface binding xpaths to specific ACL table entry keys. + // For now subscribe for full ACL table!! + notifInfo.table = db.TableSpec{Name: ACL_TABLE} + notifInfo.key = asKey("*") + notifInfo.needCache = true + + } else { + log.Errorf("Unknown path %s", pathInfo.Template) + return nil, nil, notSupported + } + + return nil, ¬ifInfo, nil +} + +func (app *AclApp) processCreate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, CREATE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *AclApp) processUpdate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, UPDATE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *AclApp) processReplace(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, REPLACE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *AclApp) processDelete(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, DELETE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *AclApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + var err error + var payload []byte + + configDb := dbs[db.ConfigDB] + err = app.processCommon(configDb, GET) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + payload, err = generateGetResponsePayload(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device), app.ygotTarget) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + return GetResponse{Payload: payload}, err +} + +func (app *AclApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) { + var resp ActionResponse + err := errors.New("Not implemented") + + return resp, err +} + +func (app *AclApp) translateCRUCommon(d *db.DB, opcode int) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCRUCommon:acl:path =", app.pathInfo.Template) + + app.convertOCAclsToInternal() + app.convertOCAclRulesToInternal(d) + app.convertOCAclBindingsToInternal() + + return keys, err +} + +func (app *AclApp) processCommon(d *db.DB, opcode int) error { + var err error + var topmostPath bool = false + acl := app.getAppRootObject() + + log.Infof("processCommon--Path Received: %s", app.pathInfo.Template) + targetType := reflect.TypeOf(*app.ygotTarget) + if !util.IsValueScalar(reflect.ValueOf(*app.ygotTarget)) && util.IsValuePtr(reflect.ValueOf(*app.ygotTarget)) { + log.Infof("processCommon: Target object is a <%s> of Type: %s", targetType.Kind().String(), targetType.Elem().Name()) + if targetType.Elem().Name() == "OpenconfigAcl_Acl" { + topmostPath = true + } + } + + targetUriPath, _ := getYangPathFromUri(app.pathInfo.Path) + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/acl-sets") { + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/acl-sets/acl-set{}{}") { + for aclSetKey, _ := range acl.AclSets.AclSet { + aclSet := acl.AclSets.AclSet[aclSetKey] + aclKey := getAclKeyStrFromOCKey(aclSetKey.Name, aclSetKey.Type) + + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/acl-sets/acl-set{}{}/acl-entries/acl-entry{}") { + // Subtree of one Rule + for seqId, _ := range aclSet.AclEntries.AclEntry { + ruleKey := "RULE_" + strconv.Itoa(int(seqId)) + entrySet := aclSet.AclEntries.AclEntry[seqId] + + ruleNodeYangPath := getYangPathFromYgotStruct(entrySet, OC_ACL_YANG_PATH_PREFIX, OC_ACL_APP_MODULE_NAME) + isRuleNodeSubtree := len(targetUriPath) > len(ruleNodeYangPath) + switch opcode { + case CREATE: + if isRuleNodeSubtree { + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, false) + } else if *app.ygotTarget == entrySet { + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, true) + } else { + log.Errorf("processCommon: Given CREATE path %s not handled", targetUriPath) + } + case REPLACE: + err = d.SetEntry(app.ruleTs, db.Key{Comp: []string{aclKey, ruleKey}}, app.ruleTableMap[aclKey][ruleKey]) + case UPDATE: + err = d.ModEntry(app.ruleTs, db.Key{Comp: []string{aclKey, ruleKey}}, app.ruleTableMap[aclKey][ruleKey]) + case DELETE: + if *app.ygotTarget == entrySet { + err = d.DeleteEntry(app.ruleTs, db.Key{Comp: []string{aclKey, ruleKey}}) + } else if isRuleNodeSubtree { + err = app.handleRuleFieldsDeletion(d, aclKey, ruleKey) + if err != nil { + return err + } + //err = d.SetEntry(app.ruleTs, db.Key{Comp: []string{aclKey, ruleKey}}, app.ruleTableMap[aclKey][ruleKey]) + } else { + log.Errorf("processCommon: Given DELETE path %s not handled", targetUriPath) + } + case GET: + err = app.convertDBAclRulesToInternal(d, aclKey, int64(seqId), db.Key{}) + ygot.BuildEmptyTree(entrySet) + app.convertInternalToOCAclRule(aclKey, aclSetKey.Type, int64(seqId), nil, entrySet) + } + } + } else { + isAclEntriesSubtree := isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/acl-sets/acl-set{}{}/acl-entries") + switch opcode { + case CREATE: + if *app.ygotTarget == aclSet { + err = app.setAclDataInConfigDb(d, app.aclTableMap, true) + if err != nil { + return err + } + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, true) + } else if isAclEntriesSubtree { + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, true) + } else { + err = d.SetEntry(app.aclTs, db.Key{Comp: []string{aclKey}}, app.aclTableMap[aclKey]) + } + case REPLACE: + if *app.ygotTarget == aclSet || isAclEntriesSubtree { + err = d.DeleteKeys(app.ruleTs, db.Key{Comp: []string{aclKey + TABLE_SEPARATOR + "RULE_*"}}) + if err != nil { + return err + } + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, true) + if err != nil { + return err + } + } + if !isAclEntriesSubtree { + err = d.ModEntry(app.aclTs, db.Key{Comp: []string{aclKey}}, app.aclTableMap[aclKey]) + } + case UPDATE: + if !isAclEntriesSubtree { + err = app.setAclDataInConfigDb(d, app.aclTableMap, false) + //err = d.ModEntry(app.aclTs, db.Key{Comp: []string{aclKey}}, app.aclTableMap[aclKey]) + if err != nil { + return err + } + } + if *app.ygotTarget == aclSet || isAclEntriesSubtree { + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, false) + } + case DELETE: + if *app.ygotTarget == aclSet { + err = d.DeleteKeys(app.ruleTs, db.Key{Comp: []string{aclKey + TABLE_SEPARATOR + "*"}}) + if err != nil { + return err + } + err = d.DeleteEntry(app.aclTs, db.Key{Comp: []string{aclKey}}) + } else if isAclEntriesSubtree { + err = d.DeleteKeys(app.ruleTs, db.Key{Comp: []string{aclKey + TABLE_SEPARATOR + "RULE_*"}}) + } else { + nodeInfo, err := getTargetNodeYangSchema(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device)) + if err != nil { + return err + } + if nodeInfo != nil && nodeInfo.IsLeaf() && nodeInfo.Name == "description" { + err = d.DeleteEntryFields(app.aclTs, asKey(aclKey), createEmptyDbValue(ACL_DESCRIPTION)) + } + //err = d.SetEntry(app.aclTs, db.Key{Comp: []string{aclKey}}, app.aclTableMap[aclKey]) + } + case GET: + err = app.convertDBAclToInternal(d, db.Key{Comp: []string{aclKey}}) + if err != nil { + return err + } + ygot.BuildEmptyTree(aclSet) + app.convertInternalToOCAcl(aclKey, acl.AclSets, aclSet) + } + } + } + } else { + // All Acls and their rules + err = app.processCommonToplevelPath(d, acl, opcode, false) + } + } else if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/interfaces") { + switch opcode { + case CREATE, REPLACE, UPDATE: + err = app.setAclBindDataInConfigDb(d, app.aclTableMap, opcode) + case DELETE: + err = app.handleBindingsDeletion(d) + case GET: + if isSubtreeRequest(app.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 = app.getAclBindingInfoForInterfaceData(d, intfData, intfId, "INGRESS") + } else if isSubtreeRequest(targetUriPath, "/openconfig-acl:acl/interfaces/interface/egress-acl-sets") { + err = app.getAclBindingInfoForInterfaceData(d, intfData, intfId, "EGRESS") + } else { + // Direction unknown. Check ACL Table for binding information. + err = app.getAclBindingInfoForInterfaceData(d, intfData, intfId, "INGRESS") + if err != nil { + return err + } + err = app.getAclBindingInfoForInterfaceData(d, intfData, intfId, "EGRESS") + } + } + } else { + err = app.getAllBindingsInfo(d) + } + } + } else { + err = app.processCommonToplevelPath(d, acl, opcode, true) + } + + if !topmostPath && !isSubtreeRequest(targetUriPath, "/openconfig-acl:acl/acl-sets") && !isSubtreeRequest(targetUriPath, "/openconfig-acl:acl/interfaces") { + err = tlerr.NotSupported("URL %s is not supported", app.pathInfo.Template) + } + + return err +} + +func (app *AclApp) processCommonToplevelPath(d *db.DB, acl *ocbinds.OpenconfigAcl_Acl, opcode int, isTopmostPath bool) error { + var err error + switch opcode { + case CREATE: + err = app.setAclDataInConfigDb(d, app.aclTableMap, true) + if err != nil { + return err + } + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, true) + case REPLACE: + err = d.DeleteTable(app.aclTs) + if err != nil { + return err + } + err = d.DeleteTable(app.ruleTs) + if err != nil { + return err + } + err = app.setAclDataInConfigDb(d, app.aclTableMap, true) + if err != nil { + return err + } + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, true) + case UPDATE: + err = app.setAclDataInConfigDb(d, app.aclTableMap, false) + if err != nil { + return err + } + err = app.setAclRuleDataInConfigDb(d, app.ruleTableMap, false) + case DELETE: + err = d.DeleteTable(app.ruleTs) + if err != nil { + return err + } + err = d.DeleteTable(app.aclTs) + case GET: + ygot.BuildEmptyTree(acl) + err = app.convertDBAclToInternal(d, db.Key{}) + if err != nil { + return err + } + app.convertInternalToOCAcl("", acl.AclSets, nil) + if isTopmostPath { + err = app.getAllBindingsInfo(d) + } + } + return err +} + +/*********** These are Translation Helper Function ***********/ +func (app *AclApp) convertDBAclRulesToInternal(dbCl *db.DB, aclName string, seqId int64, ruleKey db.Key) error { + var err error + if seqId != -1 { + ruleKey.Comp = []string{aclName, "RULE_" + strconv.FormatInt(int64(seqId), 10)} + } + if ruleKey.Len() > 1 { + ruleName := ruleKey.Get(1) + ruleData, err := dbCl.GetEntry(app.ruleTs, ruleKey) + if err != nil { + return err + } + if app.ruleTableMap[aclName] == nil { + app.ruleTableMap[aclName] = make(map[string]db.Value) + } + app.ruleTableMap[aclName][ruleName] = ruleData + } else { + ruleKeys, err := dbCl.GetKeys(app.ruleTs) + if err != nil { + return err + } + for i, _ := range ruleKeys { + if aclName == ruleKeys[i].Get(0) { + app.convertDBAclRulesToInternal(dbCl, aclName, -1, ruleKeys[i]) + } + } + } + return err +} + +func (app *AclApp) convertDBAclToInternal(dbCl *db.DB, aclkey db.Key) error { + var err error + if aclkey.Len() > 0 { + // Get one particular ACL + entry, err := dbCl.GetEntry(app.aclTs, aclkey) + if err != nil { + return err + } + if entry.IsPopulated() { + app.aclTableMap[aclkey.Get(0)] = entry + app.ruleTableMap[aclkey.Get(0)] = make(map[string]db.Value) + err = app.convertDBAclRulesToInternal(dbCl, aclkey.Get(0), -1, db.Key{}) + if err != nil { + return err + } + } else { + return tlerr.NotFound("Acl %s is not configured", aclkey.Get(0)) + } + } else { + // Get all ACLs + tbl, err := dbCl.GetTable(app.aclTs) + if err != nil { + return err + } + keys, _ := tbl.GetKeys() + for i, _ := range keys { + app.convertDBAclToInternal(dbCl, keys[i]) + } + } + return err +} + +func (app *AclApp) convertInternalToOCAcl(aclName string, aclSets *ocbinds.OpenconfigAcl_Acl_AclSets, aclSet *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet) { + if len(aclName) > 0 { + aclData := app.aclTableMap[aclName] + if aclSet != nil { + aclSet.Config.Name = aclSet.Name + aclSet.Config.Type = aclSet.Type + aclSet.State.Name = aclSet.Name + aclSet.State.Type = aclSet.Type + + for k := range aclData.Field { + if ACL_DESCRIPTION == k { + descr := aclData.Get(k) + aclSet.Config.Description = &descr + aclSet.State.Description = &descr + } else if "ports@" == k { + continue + } + } + + app.convertInternalToOCAclRule(aclName, aclSet.Type, -1, aclSet, nil) + } + } else { + for acln := range app.aclTableMap { + acldata := app.aclTableMap[acln] + var aclNameStr string + var aclType ocbinds.E_OpenconfigAcl_ACL_TYPE + if acldata.Get(ACL_TYPE) == SONIC_ACL_TYPE_IPV4 { + aclNameStr = strings.Replace(acln, "_"+OPENCONFIG_ACL_TYPE_IPV4, "", 1) + aclType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 + } else if acldata.Get(ACL_TYPE) == SONIC_ACL_TYPE_IPV6 { + aclNameStr = strings.Replace(acln, "_"+OPENCONFIG_ACL_TYPE_IPV6, "", 1) + aclType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 + } else if acldata.Get(ACL_TYPE) == SONIC_ACL_TYPE_L2 { + aclNameStr = strings.Replace(acln, "_"+OPENCONFIG_ACL_TYPE_L2, "", 1) + aclType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 + } + aclSetPtr, aclErr := aclSets.NewAclSet(aclNameStr, aclType) + if aclErr != nil { + fmt.Println("Error handling: ", aclErr) + } + ygot.BuildEmptyTree(aclSetPtr) + app.convertInternalToOCAcl(acln, nil, aclSetPtr) + } + } +} + +func (app *AclApp) convertInternalToOCAclRule(aclName string, aclType ocbinds.E_OpenconfigAcl_ACL_TYPE, seqId int64, aclSet *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet, entrySet *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if seqId != -1 { + ruleName := "RULE_" + strconv.FormatInt(int64(seqId), 10) + app.convertInternalToOCAclRuleProperties(app.ruleTableMap[aclName][ruleName], aclType, nil, entrySet) + } else { + for ruleName := range app.ruleTableMap[aclName] { + app.convertInternalToOCAclRuleProperties(app.ruleTableMap[aclName][ruleName], aclType, aclSet, nil) + } + } +} + +func (app *AclApp) convertInternalToOCAclRuleProperties(ruleData db.Value, aclType ocbinds.E_OpenconfigAcl_ACL_TYPE, aclSet *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet, entrySet *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + priority, _ := strconv.ParseInt(ruleData.Get("PRIORITY"), 10, 32) + seqId := uint32(MAX_PRIORITY - priority) + //ruleDescr := ruleData.Get("RULE_DESCRIPTION") + + if entrySet == nil { + if aclSet != nil { + entrySet_, _ := aclSet.AclEntries.NewAclEntry(seqId) + entrySet = entrySet_ + ygot.BuildEmptyTree(entrySet) + } + } + + entrySet.Config.SequenceId = &seqId + //entrySet.Config.Description = &ruleDescr + entrySet.State.SequenceId = &seqId + //entrySet.State.Description = &ruleDescr + + var num uint64 + num = 0 + entrySet.State.MatchedOctets = &num + entrySet.State.MatchedPackets = &num + + ygot.BuildEmptyTree(entrySet.Transport) + ygot.BuildEmptyTree(entrySet.Actions) + + for ruleKey := range ruleData.Field { + if "L4_SRC_PORT" == ruleKey || "L4_SRC_PORT_RANGE" == ruleKey { + port := ruleData.Get(ruleKey) + srcPort := getTransportSrcDestPorts(port, "src") + entrySet.Transport.Config.SourcePort, _ = entrySet.Transport.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union(srcPort) + entrySet.Transport.State.SourcePort, _ = entrySet.Transport.State.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_State_SourcePort_Union(srcPort) + } else if "L4_DST_PORT" == ruleKey || "L4_DST_PORT_RANGE" == ruleKey { + port := ruleData.Get(ruleKey) + destPort := getTransportSrcDestPorts(port, "dest") + entrySet.Transport.Config.DestinationPort, _ = entrySet.Transport.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union(destPort) + entrySet.Transport.State.DestinationPort, _ = entrySet.Transport.State.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_State_DestinationPort_Union(destPort) + } else if "TCP_FLAGS" == ruleKey { + tcpFlags := ruleData.Get(ruleKey) + entrySet.Transport.Config.TcpFlags = getTransportConfigTcpFlags(tcpFlags) + entrySet.Transport.State.TcpFlags = getTransportConfigTcpFlags(tcpFlags) + } else if "PACKET_ACTION" == ruleKey { + if "FORWARD" == ruleData.Get(ruleKey) { + entrySet.Actions.Config.ForwardingAction = ocbinds.OpenconfigAcl_FORWARDING_ACTION_ACCEPT + entrySet.Actions.State.ForwardingAction = ocbinds.OpenconfigAcl_FORWARDING_ACTION_ACCEPT + } else { + entrySet.Actions.Config.ForwardingAction = ocbinds.OpenconfigAcl_FORWARDING_ACTION_DROP + entrySet.Actions.State.ForwardingAction = ocbinds.OpenconfigAcl_FORWARDING_ACTION_DROP + } + } + } + + if aclType == ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 { + ygot.BuildEmptyTree(entrySet.Ipv4) + for ruleKey := range ruleData.Field { + if "IP_PROTOCOL" == ruleKey { + ipProto, _ := strconv.ParseInt(ruleData.Get(ruleKey), 10, 64) + protocolVal := getIpProtocol(ipProto) + entrySet.Ipv4.Config.Protocol, _ = entrySet.Ipv4.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union(protocolVal) + entrySet.Ipv4.State.Protocol, _ = entrySet.Ipv4.State.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_State_Protocol_Union(protocolVal) + } else if "DSCP" == ruleKey { + var dscp uint8 + dscpData, _ := strconv.ParseInt(ruleData.Get(ruleKey), 10, 64) + dscp = uint8(dscpData) + entrySet.Ipv4.Config.Dscp = &dscp + entrySet.Ipv4.State.Dscp = &dscp + } else if "SRC_IP" == ruleKey { + addr := ruleData.Get(ruleKey) + entrySet.Ipv4.Config.SourceAddress = &addr + entrySet.Ipv4.State.SourceAddress = &addr + } else if "DST_IP" == ruleKey { + addr := ruleData.Get(ruleKey) + entrySet.Ipv4.Config.DestinationAddress = &addr + entrySet.Ipv4.State.DestinationAddress = &addr + } + } + } else if aclType == ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 { + ygot.BuildEmptyTree(entrySet.Ipv6) + for ruleKey := range ruleData.Field { + if "IP_PROTOCOL" == ruleKey { + ipProto, _ := strconv.ParseInt(ruleData.Get(ruleKey), 10, 64) + protocolVal := getIpProtocol(ipProto) + entrySet.Ipv6.Config.Protocol, _ = entrySet.Ipv6.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_Config_Protocol_Union(protocolVal) + entrySet.Ipv6.State.Protocol, _ = entrySet.Ipv6.State.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_State_Protocol_Union(protocolVal) + } else if "DSCP" == ruleKey { + var dscp uint8 + dscpData, _ := strconv.ParseInt(ruleData.Get(ruleKey), 10, 64) + dscp = uint8(dscpData) + entrySet.Ipv6.Config.Dscp = &dscp + entrySet.Ipv6.State.Dscp = &dscp + } else if "SRC_IPV6" == ruleKey { + addr := ruleData.Get(ruleKey) + entrySet.Ipv6.Config.SourceAddress = &addr + entrySet.Ipv6.State.SourceAddress = &addr + } else if "DST_IPV6" == ruleKey { + addr := ruleData.Get(ruleKey) + entrySet.Ipv6.Config.DestinationAddress = &addr + entrySet.Ipv6.State.DestinationAddress = &addr + } + } + } else if aclType == ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 { + ygot.BuildEmptyTree(entrySet.L2) + for ruleKey := range ruleData.Field { + if "ETHER_TYPE" == ruleKey { + ethType, _ := strconv.ParseUint(strings.Replace(ruleData.Get(ruleKey), "0x", "", -1), 16, 32) + ethertype := getL2EtherType(ethType) + entrySet.L2.Config.Ethertype, _ = entrySet.L2.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union(ethertype) + entrySet.L2.State.Ethertype, _ = entrySet.L2.State.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_State_Ethertype_Union(ethertype) + } + } + } +} + +func convertInternalToOCAclRuleBinding(d *db.DB, 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 (app *AclApp) convertInternalToOCAclBinding(d *db.DB, aclName string, intfId string, direction string, intfAclSet ygot.GoStruct) error { + var err error + if _, ok := app.aclTableMap[aclName]; !ok { + aclEntry, err1 := d.GetEntry(app.aclTs, db.Key{Comp: []string{aclName}}) + if err1 != nil { + return err1 + } + if !contains(aclEntry.GetList("ports"), intfId) { + return tlerr.InvalidArgs("Acl %s not binded with %s", aclName, intfId) + } + } + + if _, ok := app.ruleTableMap[aclName]; !ok { + ruleKeys, _ := d.GetKeys(app.ruleTs) + for i, _ := range ruleKeys { + rulekey := ruleKeys[i] + // Rulekey has two keys, first aclkey and second rulename + if rulekey.Get(0) == aclName { + seqId, _ := strconv.Atoi(strings.Replace(rulekey.Get(1), "RULE_", "", 1)) + convertInternalToOCAclRuleBinding(d, 0, int64(seqId), direction, intfAclSet, nil) + } + } + } else { + for ruleName := range app.ruleTableMap[aclName] { + seqId, _ := strconv.Atoi(strings.Replace(ruleName, "RULE_", "", 1)) + convertInternalToOCAclRuleBinding(d, 0, int64(seqId), direction, intfAclSet, nil) + } + } + + return err +} + +func (app *AclApp) getAllBindingsInfo(d *db.DB) error { + var err error + acl := app.getAppRootObject() + if len(app.aclTableMap) == 0 { + aclKeys, _ := d.GetKeys(app.aclTs) + for i, _ := range aclKeys { + aclEntry, _ := d.GetEntry(app.aclTs, aclKeys[i]) + app.aclTableMap[(aclKeys[i]).Get(0)] = aclEntry + } + } + var interfaces []string + for aclName := range app.aclTableMap { + aclData := app.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]) + } + } + } + } + + 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 = app.getAclBindingInfoForInterfaceData(d, intfData, intfId, "INGRESS") + err = app.getAclBindingInfoForInterfaceData(d, intfData, intfId, "EGRESS") + } + return err +} + +func (app *AclApp) getAclBindingInfoForInterfaceData(d *db.DB, 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] + _, err := d.GetEntry(app.ruleTs, db.Key{Comp: []string{aclKey, rulekey}}) + if err != nil { + return err + } + convertInternalToOCAclRuleBinding(d, 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 = app.convertInternalToOCAclBinding(d, aclKey, intfId, direction, ingressAclSet) + } + } + } else { + err = app.findAndGetAclBindingInfoForInterfaceData(d, 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] + _, err := d.GetEntry(app.ruleTs, db.Key{Comp: []string{aclKey, rulekey}}) + if err != nil { + return err + } + convertInternalToOCAclRuleBinding(d, 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 = app.convertInternalToOCAclBinding(d, aclKey, intfId, direction, egressAclSet) + } + } + } else { + err = app.findAndGetAclBindingInfoForInterfaceData(d, intfId, direction, intfData) + } + } else { + log.Error("Unknown direction") + } + return err +} + +func (app *AclApp) findAndGetAclBindingInfoForInterfaceData(d *db.DB, intfId string, direction string, intfData *ocbinds.OpenconfigAcl_Acl_Interfaces_Interface) error { + var err error + if len(app.aclTableMap) == 0 { + aclKeys, _ := d.GetKeys(app.aclTs) + for i, _ := range aclKeys { + aclEntry, _ := d.GetEntry(app.aclTs, aclKeys[i]) + app.aclTableMap[aclKeys[i].Get(0)] = aclEntry + } + } + + for aclName, _ := range app.aclTableMap { + aclData := app.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 = app.convertInternalToOCAclBinding(d, 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 = app.convertInternalToOCAclBinding(d, aclName, intfId, direction, egressAclSet) + if err != nil { + return err + } + } + } + } + } + return err +} + +/*func (app *AclApp) isInterfaceBindWithACL(d *db.DB, intfId string) bool { + var isFound bool = false + + if len(app.aclTableMap) == 0 { + aclKeys, _ := d.GetKeys(app.aclTs) + for i, _ := range aclKeys { + aclEntry, _ := d.GetEntry(app.aclTs, aclKeys[i]) + app.aclTableMap[(aclKeys[i]).Get(0)] = aclEntry + } + } + + var interfaces []string + for aclName := range app.aclTableMap { + aclData := app.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]) + } + } + } + } + + isFound = contains(interfaces, intfId) + return isFound +}*/ + +func (app *AclApp) handleBindingsDeletion(d *db.DB) error { + var err error + + acl := app.getAppRootObject() + aclKeys, _ := d.GetKeys(app.aclTs) + for i, _ := range aclKeys { + aclEntry, _ := d.GetEntry(app.aclTs, aclKeys[i]) + var isRequestedAclFound = false + if len(aclEntry.GetList("ports")) > 0 { + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/interfaces/interface{}") { + direction := aclEntry.Get("stage") + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/interfaces/interface{}/ingress-acl-sets") && direction != "INGRESS" { + return tlerr.InvalidArgs("Acl %s is not Ingress", aclKeys[i].Get(0)) + } + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/interfaces/interface{}/egress-acl-sets") && direction != "EGRESS" { + return tlerr.InvalidArgs("Acl %s is not Egress", aclKeys[i].Get(0)) + } + for intfId := range acl.Interfaces.Interface { + aclname, acltype := getAclKeysFromStrKey(aclKeys[i].Get(0), aclEntry.Get("type")) + intfData := acl.Interfaces.Interface[intfId] + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/interfaces/interface{}/ingress-acl-sets/ingress-acl-set{}{}") { + for k := range intfData.IngressAclSets.IngressAclSet { + if aclname == k.SetName { + if acltype == k.Type { + isRequestedAclFound = true + } else { + return tlerr.InvalidArgs("Acl Type is not matching") + } + } else { + goto SkipDBProcessing + } + } + } else if isSubtreeRequest(app.pathInfo.Template, "/openconfig-acl:acl/interfaces/interface{}/egress-acl-sets/egress-acl-set{}{}") { + for k := range intfData.EgressAclSets.EgressAclSet { + if aclname == k.SetName { + if acltype == k.Type { + isRequestedAclFound = true + } else { + return tlerr.InvalidArgs("Acl Type is not matching") + } + } else { + goto SkipDBProcessing + } + } + } + intfs := aclEntry.GetList("ports") + intfs = removeElement(intfs, intfId) + aclEntry.SetList("ports", intfs) + err = d.SetEntry(app.aclTs, aclKeys[i], aclEntry) + if err != nil { + return err + } + // If last interface removed, then remove stage field also + if len(intfs) == 0 { + aclEntry.Remove("stage") + } + } + SkipDBProcessing: + } else { + aclEntry.Remove("stage") + aclEntry.SetList("ports", []string{}) + err = d.SetEntry(app.aclTs, aclKeys[i], aclEntry) + if err != nil { + return err + } + } + } + if isRequestedAclFound { + break + } + } + + return err +} + +/******************** CREATE related *******************************/ +func (app *AclApp) convertOCAclsToInternal() { + acl := app.getAppRootObject() + if acl != nil { + app.aclTableMap = make(map[string]db.Value) + if acl.AclSets != nil && len(acl.AclSets.AclSet) > 0 { + for aclSetKey, _ := range acl.AclSets.AclSet { + aclSet := acl.AclSets.AclSet[aclSetKey] + aclKey := getAclKeyStrFromOCKey(aclSetKey.Name, aclSetKey.Type) + app.aclTableMap[aclKey] = db.Value{Field: map[string]string{}} + + if aclSet.Config != nil { + if aclSet.Config.Type == ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 { + app.aclTableMap[aclKey].Field[ACL_TYPE] = SONIC_ACL_TYPE_IPV4 + } else if aclSet.Config.Type == ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 { + app.aclTableMap[aclKey].Field[ACL_TYPE] = SONIC_ACL_TYPE_IPV6 + } else if aclSet.Config.Type == ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 { + app.aclTableMap[aclKey].Field[ACL_TYPE] = SONIC_ACL_TYPE_L2 + } + + if aclSet.Config.Description != nil && len(*aclSet.Config.Description) > 0 { + app.aclTableMap[aclKey].Field[ACL_DESCRIPTION] = *aclSet.Config.Description + } + } + } + } + } +} + +func (app *AclApp) convertOCAclRulesToInternal(d *db.DB) { + acl := app.getAppRootObject() + if acl != nil { + app.ruleTableMap = make(map[string]map[string]db.Value) + if acl.AclSets != nil && len(acl.AclSets.AclSet) > 0 { + for aclSetKey, _ := range acl.AclSets.AclSet { + aclSet := acl.AclSets.AclSet[aclSetKey] + aclKey := getAclKeyStrFromOCKey(aclSetKey.Name, aclSetKey.Type) + app.ruleTableMap[aclKey] = make(map[string]db.Value) + + if aclSet.AclEntries != nil { + for seqId, _ := range aclSet.AclEntries.AclEntry { + entrySet := aclSet.AclEntries.AclEntry[seqId] + ruleName := "RULE_" + strconv.Itoa(int(seqId)) + app.ruleTableMap[aclKey][ruleName] = db.Value{Field: map[string]string{}} + convertOCAclRuleToInternalAclRule(app.ruleTableMap[aclKey][ruleName], seqId, aclKey, aclSet.Type, entrySet) + } + } + } + } + } +} + +func (app *AclApp) convertOCAclBindingsToInternal() { + aclObj := app.getAppRootObject() + + if aclObj.Interfaces != nil && len(aclObj.Interfaces.Interface) > 0 { + aclInterfacesMap := make(map[string][]string) + // Below code assumes that an ACL can be either INGRESS or EGRESS but not both. + 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) + // TODO: Need to handle Subinterface also + if intf.InterfaceRef != nil && intf.InterfaceRef.Config.Interface != nil { + aclInterfacesMap[aclName] = append(aclInterfacesMap[aclName], *intf.InterfaceRef.Config.Interface) + } else { + aclInterfacesMap[aclName] = append(aclInterfacesMap[aclName], *intf.Id) + } + if len(app.aclTableMap) == 0 { + app.aclTableMap[aclName] = db.Value{Field: map[string]string{}} + } + app.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) + if intf.InterfaceRef != nil && intf.InterfaceRef.Config.Interface != nil { + aclInterfacesMap[aclName] = append(aclInterfacesMap[aclName], *intf.InterfaceRef.Config.Interface) + } else { + aclInterfacesMap[aclName] = append(aclInterfacesMap[aclName], *intf.Id) + } + if len(app.aclTableMap) == 0 { + app.aclTableMap[aclName] = db.Value{Field: map[string]string{}} + } + app.aclTableMap[aclName].Field["stage"] = "EGRESS" + } + } + } + } + for k, _ := range aclInterfacesMap { + val := app.aclTableMap[k] + (&val).SetList("ports", aclInterfacesMap[k]) + } + } +} + +func convertOCAclRuleToInternalAclRule(ruleData db.Value, seqId uint32, aclName string, aclType ocbinds.E_OpenconfigAcl_ACL_TYPE, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + ruleIndex := seqId + ruleData.Field["PRIORITY"] = strconv.FormatInt(int64(MAX_PRIORITY-ruleIndex), 10) + // Rule Description is not supported in Sonic. So commenting this out. + /* + if rule.Config != nil && rule.Config.Description != nil { + ruleData.Field["RULE_DESCRIPTION"] = *rule.Config.Description + } + */ + + if ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 == aclType { + convertOCToInternalIPv4(ruleData, aclName, ruleIndex, rule) + } else if ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 == aclType { + convertOCToInternalIPv6(ruleData, aclName, ruleIndex, rule) + } else if ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 == aclType { + convertOCToInternalL2(ruleData, aclName, ruleIndex, rule) + } /*else if ocbinds.OpenconfigAcl_ACL_TYPE_ACL_MIXED == aclType { + } */ + + convertOCToInternalTransport(ruleData, aclName, ruleIndex, rule) + convertOCToInternalInputInterface(ruleData, aclName, ruleIndex, rule) + convertOCToInternalInputAction(ruleData, aclName, ruleIndex, rule) +} + +func convertOCToInternalL2(ruleData db.Value, aclName string, ruleIndex uint32, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if rule.L2 == nil { + return + } + if rule.L2.Config.Ethertype != nil && util.IsTypeStructPtr(reflect.TypeOf(rule.L2.Config.Ethertype)) { + ethertypeType := reflect.TypeOf(rule.L2.Config.Ethertype).Elem() + var b bytes.Buffer + switch ethertypeType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_E_OpenconfigPacketMatchTypes_ETHERTYPE{}): + v := (rule.L2.Config.Ethertype).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_E_OpenconfigPacketMatchTypes_ETHERTYPE) + //ruleData["ETHER_TYPE"] = v.E_OpenconfigPacketMatchTypes_ETHERTYPE.ΛMap()["E_OpenconfigPacketMatchTypes_ETHERTYPE"][int64(v.E_OpenconfigPacketMatchTypes_ETHERTYPE)].Name + fmt.Fprintf(&b, "0x%0.4x", ETHERTYPE_MAP[v.E_OpenconfigPacketMatchTypes_ETHERTYPE]) + ruleData.Field["ETHER_TYPE"] = b.String() + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_Uint16{}): + v := (rule.L2.Config.Ethertype).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config_Ethertype_Union_Uint16) + fmt.Fprintf(&b, "0x%0.4x", v.Uint16) + ruleData.Field["ETHER_TYPE"] = b.String() + } + } +} + +func convertOCToInternalIPv4(ruleData db.Value, aclName string, ruleIndex uint32, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if rule.Ipv4.Config.Protocol != nil && util.IsTypeStructPtr(reflect.TypeOf(rule.Ipv4.Config.Protocol)) { + protocolType := reflect.TypeOf(rule.Ipv4.Config.Protocol).Elem() + switch protocolType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_E_OpenconfigPacketMatchTypes_IP_PROTOCOL{}): + v := (rule.Ipv4.Config.Protocol).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_E_OpenconfigPacketMatchTypes_IP_PROTOCOL) + //ruleData["IP_PROTOCOL"] = v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL.ΛMap()["E_OpenconfigPacketMatchTypes_IP_PROTOCOL"][int64(v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL)].Name + ruleData.Field["IP_PROTOCOL"] = strconv.FormatInt(int64(IP_PROTOCOL_MAP[v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL]), 10) + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_Uint8{}): + v := (rule.Ipv4.Config.Protocol).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union_Uint8) + ruleData.Field["IP_PROTOCOL"] = strconv.FormatInt(int64(v.Uint8), 10) + } + } + + if rule.Ipv4.Config.Dscp != nil { + ruleData.Field["DSCP"] = strconv.FormatInt(int64(*rule.Ipv4.Config.Dscp), 10) + } + if rule.Ipv4.Config.SourceAddress != nil { + ruleData.Field["SRC_IP"] = *rule.Ipv4.Config.SourceAddress + } + if rule.Ipv4.Config.DestinationAddress != nil { + ruleData.Field["DST_IP"] = *rule.Ipv4.Config.DestinationAddress + } +} + +func convertOCToInternalIPv6(ruleData db.Value, aclName string, ruleIndex uint32, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if rule.Ipv6.Config.Protocol != nil && util.IsTypeStructPtr(reflect.TypeOf(rule.Ipv6.Config.Protocol)) { + protocolType := reflect.TypeOf(rule.Ipv6.Config.Protocol).Elem() + switch protocolType { + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_Config_Protocol_Union_E_OpenconfigPacketMatchTypes_IP_PROTOCOL{}): + v := (rule.Ipv6.Config.Protocol).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_Config_Protocol_Union_E_OpenconfigPacketMatchTypes_IP_PROTOCOL) + //ruleData["IP_PROTOCOL"] = v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL.ΛMap()["E_OpenconfigPacketMatchTypes_IP_PROTOCOL"][int64(v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL)].Name + ruleData.Field["IP_PROTOCOL"] = strconv.FormatInt(int64(IP_PROTOCOL_MAP[v.E_OpenconfigPacketMatchTypes_IP_PROTOCOL]), 10) + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_Config_Protocol_Union_Uint8{}): + v := (rule.Ipv6.Config.Protocol).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_Config_Protocol_Union_Uint8) + ruleData.Field["IP_PROTOCOL"] = strconv.FormatInt(int64(v.Uint8), 10) + } + } + + if rule.Ipv6.Config.Dscp != nil { + ruleData.Field["DSCP"] = strconv.FormatInt(int64(*rule.Ipv6.Config.Dscp), 10) + } + if rule.Ipv6.Config.SourceAddress != nil { + ruleData.Field["SRC_IPV6"] = *rule.Ipv6.Config.SourceAddress + } + if rule.Ipv6.Config.DestinationAddress != nil { + ruleData.Field["DST_IPV6"] = *rule.Ipv6.Config.DestinationAddress + } + if rule.Ipv6.Config.SourceFlowLabel != nil { + ruleData.Field["SRC_FLOWLABEL"] = strconv.FormatInt(int64(*rule.Ipv6.Config.SourceFlowLabel), 10) + } + if rule.Ipv6.Config.DestinationFlowLabel != nil { + ruleData.Field["DST_FLOWLABEL"] = strconv.FormatInt(int64(*rule.Ipv6.Config.DestinationFlowLabel), 10) + } +} + +func convertOCToInternalTransport(ruleData db.Value, aclName string, ruleIndex uint32, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if rule.Transport == nil { + return + } + if rule.Transport.Config.SourcePort != nil && util.IsTypeStructPtr(reflect.TypeOf(rule.Transport.Config.SourcePort)) { + sourceportType := reflect.TypeOf(rule.Transport.Config.SourcePort).Elem() + 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 := (rule.Transport.Config.SourcePort).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort) + ruleData.Field["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 + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_String{}): + v := (rule.Transport.Config.SourcePort).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_String) + ruleData.Field["L4_SRC_PORT_RANGE"] = strings.Replace(v.String, "..", "-", 1) + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_Uint16{}): + v := (rule.Transport.Config.SourcePort).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_Union_Uint16) + ruleData.Field["L4_SRC_PORT"] = strconv.FormatInt(int64(v.Uint16), 10) + } + } + + if rule.Transport.Config.DestinationPort != nil && util.IsTypeStructPtr(reflect.TypeOf(rule.Transport.Config.DestinationPort)) { + destportType := reflect.TypeOf(rule.Transport.Config.DestinationPort).Elem() + 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 := (rule.Transport.Config.DestinationPort).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_E_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort) + ruleData.Field["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 + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_String{}): + v := (rule.Transport.Config.DestinationPort).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_String) + ruleData.Field["L4_DST_PORT_RANGE"] = strings.Replace(v.String, "..", "-", 1) + case reflect.TypeOf(ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_Uint16{}): + v := (rule.Transport.Config.DestinationPort).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union_Uint16) + ruleData.Field["L4_DST_PORT"] = strconv.FormatInt(int64(v.Uint16), 10) + } + } + + var tcpFlags uint32 = 0x00 + if len(rule.Transport.Config.TcpFlags) > 0 { + for _, flag := range rule.Transport.Config.TcpFlags { + switch flag { + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_FIN: + tcpFlags |= 0x01 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_SYN: + tcpFlags |= 0x02 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_RST: + tcpFlags |= 0x04 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_PSH: + tcpFlags |= 0x08 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_ACK: + tcpFlags |= 0x10 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_URG: + tcpFlags |= 0x20 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_ECE: + tcpFlags |= 0x40 + case ocbinds.OpenconfigPacketMatchTypes_TCP_FLAGS_TCP_CWR: + tcpFlags |= 0x80 + } + } + var b bytes.Buffer + fmt.Fprintf(&b, "0x%0.2x/0x%0.2x", tcpFlags, tcpFlags) + ruleData.Field["TCP_FLAGS"] = b.String() + } +} + +func convertOCToInternalInputInterface(ruleData db.Value, aclName string, ruleIndex uint32, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if rule.InputInterface != nil && rule.InputInterface.InterfaceRef != nil { + ruleData.Field["IN_PORTS"] = *rule.InputInterface.InterfaceRef.Config.Interface + } +} + +func convertOCToInternalInputAction(ruleData db.Value, aclName string, ruleIndex uint32, rule *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) { + if rule.Actions != nil && rule.Actions.Config != nil { + switch rule.Actions.Config.ForwardingAction { + case ocbinds.OpenconfigAcl_FORWARDING_ACTION_ACCEPT: + ruleData.Field["PACKET_ACTION"] = "FORWARD" + case ocbinds.OpenconfigAcl_FORWARDING_ACTION_DROP, ocbinds.OpenconfigAcl_FORWARDING_ACTION_REJECT: + ruleData.Field["PACKET_ACTION"] = "DROP" + default: + } + } +} + +func (app *AclApp) handleRuleFieldsDeletion(d *db.DB, aclKey string, ruleKey string) error { + var err error + + ruleEntry, err := d.GetEntry(app.ruleTs, asKey(aclKey, ruleKey)) + if err != nil { + return err + } + nodeInfo, err := getTargetNodeYangSchema(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device)) + if err != nil { + return err + } + if nodeInfo.IsLeaf() { + switch nodeInfo.Name { + case "description": + (&ruleEntry).Remove("RULE_DESCRIPTION") + // L2 + case "ethertype": + (&ruleEntry).Remove("ETHER_TYPE") + // IPv4/IPv6 + case "source-address": + if strings.Contains(app.pathInfo.Path, "ipv4/config") { + (&ruleEntry).Remove("SRC_IP") + } else if strings.Contains(app.pathInfo.Path, "ipv6/config") { + (&ruleEntry).Remove("SRC_IPV6") + } + case "destination-address": + if strings.Contains(app.pathInfo.Path, "ipv4/config") { + (&ruleEntry).Remove("DST_IP") + } else if strings.Contains(app.pathInfo.Path, "ipv6/config") { + (&ruleEntry).Remove("DST_IPV6") + } + case "dscp": + (&ruleEntry).Remove("DSCP") + case "protocol": + (&ruleEntry).Remove("IP_PROTOCOL") + // transport + case "source-port": + (&ruleEntry).Remove("L4_SRC_PORT") + (&ruleEntry).Remove("L4_SRC_PORT_RANGE") + case "destination-port": + (&ruleEntry).Remove("L4_DST_PORT") + (&ruleEntry).Remove("L4_DST_PORT_RANGE") + // actions + case "forwarding-action": + (&ruleEntry).Remove("PACKET_ACTION") + //input-interface + case "interface": + (&ruleEntry).Remove("IN_PORTS") + //case "subinterface": + } + } else if nodeInfo.IsContainer() { + targetType := reflect.TypeOf(*app.ygotTarget) + switch targetType.Elem().Name() { + case "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_L2_Config": + (&ruleEntry).Remove("ETHER_TYPE") + case "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config": + (&ruleEntry).Remove("IP_PROTOCOL") + (&ruleEntry).Remove("SRC_IP") + (&ruleEntry).Remove("DST_IP") + (&ruleEntry).Remove("DSCP") + case "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv6_Config": + (&ruleEntry).Remove("IP_PROTOCOL") + (&ruleEntry).Remove("SRC_IPV6") + (&ruleEntry).Remove("DST_IPV6") + (&ruleEntry).Remove("DSCP") + case "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config": + (&ruleEntry).Remove("L4_SRC_PORT") + (&ruleEntry).Remove("L4_SRC_PORT_RANGE") + (&ruleEntry).Remove("L4_DST_PORT") + (&ruleEntry).Remove("L4_DST_PORT_RANGE") + (&ruleEntry).Remove("TCP_FLAGS") + case "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_InputInterface", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_InputInterface_InterfaceRef", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_InputInterface_InterfaceRef_Config": + (&ruleEntry).Remove("IN_PORTS") + case "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Actions", "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Actions_Config": + (&ruleEntry).Remove("PACKET_ACTION") + } + } else if nodeInfo.IsLeafList() { + switch nodeInfo.Name { + case "tcp-flags": + (&ruleEntry).Remove("TCP_FLAGS") + } + } else { + log.Error("This yang type is not handled currently") + } + err = d.SetEntry(app.ruleTs, asKey(aclKey, ruleKey), ruleEntry) + + return err +} + +func (app *AclApp) setAclDataInConfigDb(d *db.DB, aclData map[string]db.Value, createFlag bool) error { + var err error + for key := range aclData { + existingEntry, err := d.GetEntry(app.aclTs, db.Key{Comp: []string{key}}) + // If Create ACL request comes and ACL already exists, throw error + if createFlag && existingEntry.IsPopulated() { + return tlerr.AlreadyExists("Acl %s already exists", key) + } + if createFlag || (!createFlag && err != nil && !existingEntry.IsPopulated()) { + err := d.CreateEntry(app.aclTs, db.Key{Comp: []string{key}}, aclData[key]) + if err != nil { + return err + } + } else { + if existingEntry.IsPopulated() { + if existingEntry.Get(ACL_DESCRIPTION) != aclData[key].Field[ACL_DESCRIPTION] { + err := d.ModEntry(app.aclTs, db.Key{Comp: []string{key}}, aclData[key]) + if err != nil { + return err + } + } + } + } + } + return err +} + +func (app *AclApp) setAclRuleDataInConfigDb(d *db.DB, ruleData map[string]map[string]db.Value, createFlag bool) error { + var err error + for aclName := range ruleData { + for ruleName := range ruleData[aclName] { + existingRuleEntry, err := d.GetEntry(app.ruleTs, db.Key{Comp: []string{aclName, ruleName}}) + // If Create Rule request comes and Rule already exists, throw error + if createFlag && existingRuleEntry.IsPopulated() { + return tlerr.AlreadyExists("Rule %s already exists", ruleName) + } + if createFlag || (!createFlag && err != nil && !existingRuleEntry.IsPopulated()) { + err := d.CreateEntry(app.ruleTs, db.Key{Comp: []string{aclName, ruleName}}, ruleData[aclName][ruleName]) + if err != nil { + return err + } + } else { + if existingRuleEntry.IsPopulated() { + err := d.ModEntry(app.ruleTs, db.Key{Comp: []string{aclName, ruleName}}, ruleData[aclName][ruleName]) + if err != nil { + return err + } + } + } + } + } + return err +} + +func (app *AclApp) setAclBindDataInConfigDb(d *db.DB, aclData map[string]db.Value, opcode int) error { + var err error + for aclKey, aclInfo := range aclData { + // Get ACL info from DB + dbAcl, err := d.GetEntry(app.aclTs, db.Key{Comp: []string{aclKey}}) + if err != nil { + return err + } + if REPLACE == opcode { + dbAcl.SetList("ports", aclInfo.GetList("ports")) + dbAcl.Set("stage", aclInfo.Get("stage")) + } else { + dbAclIntfs := dbAcl.GetList("ports") + if len(dbAclIntfs) > 0 { + dbAclDirec := dbAcl.Get("stage") + newDirec := aclInfo.Get("stage") + if (UPDATE == opcode) && (len(dbAclDirec) > 0) && (len(newDirec) > 0) && (dbAclDirec != newDirec) { + return tlerr.InvalidArgs("Acl direction of %s not allowed when it is already configured as %s", newDirec, dbAclDirec) + } + // Merge interfaces from DB to list in aclInfo and set back in DB + intfs := aclInfo.GetList("ports") + for _, ifId := range dbAclIntfs { + if !contains(intfs, ifId) { + intfs = append(intfs, ifId) + } + } + dbAcl.SetList("ports", intfs) + } else { + dbAcl.SetList("ports", aclInfo.GetList("ports")) + } + + if len(dbAcl.Get("stage")) == 0 { + dbAcl.Set("stage", aclInfo.Get("stage")) + } + } + err = d.SetEntry(app.aclTs, db.Key{Comp: []string{aclKey}}, dbAcl) + //err = d.ModEntry(app.aclTs, db.Key{Comp: []string{aclKey}}, dbAcl) + if err != nil { + return err + } + } + return err +} + +func getIpProtocol(proto int64) interface{} { + for k, v := range IP_PROTOCOL_MAP { + if uint8(proto) == v { + return k + } + } + return uint8(proto) +} + +func getTransportSrcDestPorts(portVal string, portType string) interface{} { + var portRange string = "" + + portNum, err := strconv.Atoi(portVal) + if err != nil && strings.Contains(portVal, "-") { + portRange = portVal + } + + if len(portRange) > 0 { + return portRange + } else if portNum > 0 { + return uint16(portNum) + } else { + if "src" == portType { + return ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_SourcePort_ANY + } else if "dest" == portType { + return ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_ANY + } + } + return nil +} + +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) +} + +func getAclKeysFromStrKey(aclKey string, aclType string) (string, ocbinds.E_OpenconfigAcl_ACL_TYPE) { + var aclOrigName string + var aclOrigType ocbinds.E_OpenconfigAcl_ACL_TYPE + + if SONIC_ACL_TYPE_IPV4 == aclType { + aclOrigName = strings.Replace(aclKey, "_"+OPENCONFIG_ACL_TYPE_IPV4, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 + } else if SONIC_ACL_TYPE_IPV6 == aclType { + aclOrigName = strings.Replace(aclKey, "_"+OPENCONFIG_ACL_TYPE_IPV6, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV6 + } else if SONIC_ACL_TYPE_L2 == aclType { + aclOrigName = strings.Replace(aclKey, "_"+OPENCONFIG_ACL_TYPE_L2, "", 1) + aclOrigType = ocbinds.OpenconfigAcl_ACL_TYPE_ACL_L2 + } + return aclOrigName, aclOrigType +} + +// getAclTypeOCEnumFromName returns the ACL_TYPE enum from name +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 +} + diff --git a/src/translib/acl_app_test.go b/src/translib/acl_app_test.go new file mode 100644 index 0000000000..26ce1f423b --- /dev/null +++ b/src/translib/acl_app_test.go @@ -0,0 +1,505 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "errors" + "fmt" + "reflect" + "strings" + "testing" + db "translib/db" +) + +func init() { + fmt.Println("+++++ Init acl_app_test +++++") +} + + +// This will test GET on /openconfig-acl:acl +func Test_AclApp_TopLevelPath(t *testing.T) { + url := "/openconfig-acl:acl" + + t.Run("Empty_Response_Top_Level", processGetRequest(url, emptyJson, false)) + + t.Run("Bulk_Create_Top_Level", processSetRequest(url, bulkAclCreateJsonRequest, "POST", false)) + + t.Run("Get_Full_Acl_Tree_Top_Level", processGetRequest(url, bulkAclCreateJsonResponse, false)) + + // Delete all bindings before deleting at top level + t.Run("Delete_All_Bindings_Top_Level", processDeleteRequest("/openconfig-acl:acl/interfaces")) + t.Run("Delete_Full_ACl_Tree_Top_Level", processDeleteRequest(url)) + + t.Run("Verify_Top_Level_Delete", processGetRequest(url, emptyJson, false)) +} + +func Test_AclApp_SingleAclOperations(t *testing.T) { + url := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL3][type=ACL_IPV4]" + + t.Run("Create_One_Acl_With_Multiple_Rules(PATCH)", processSetRequest(url, oneAclCreateWithRulesJsonRequest, "PATCH", false)) + + t.Run("Verify_Create_One_Acl_With_Multiple_Rules", processGetRequest(url, oneAclCreateWithRulesJsonResponse, false)) + + aclDescrUrl := url + "/config/description" + t.Run("Delete Acl_Description", processDeleteRequest(aclDescrUrl)) + t.Run("Verify_Acl_Description_Deletion", processGetRequest(aclDescrUrl, emptyAclDescriptionJson, false)) + + createAclDescrUrl := url + "/config" + t.Run("Create_new_Acl_Description", processSetRequest(createAclDescrUrl, aclDescrUpdateJson, "POST", false)) + t.Run("Verify_Description_of_Acl", processGetRequest(aclDescrUrl, aclDescrUpdateJson, false)) + + t.Run("Delete_One_Acl_With_All_Its_Rules", processDeleteRequest(url)) + + t.Run("Verify_One_Acl_Delete", processGetRequest(url, "", true)) +} + +func Test_AclApp_SingleRuleOperations(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=8]" + + t.Run("Create_One_Acl_Without_Rule", processSetRequest(aclUrl, oneAclCreateJsonRequest, "POST", false)) + t.Run("Get_One_Acl_Without_Rule", processGetRequest(aclUrl, oneAclCreateJsonResponse, false)) + + t.Run("Create_One_Rule", processSetRequest(ruleUrl, requestOneRulePostJson, "POST", false)) + t.Run("Get_One_Rule", processGetRequest(ruleUrl, responseOneRuleJson, false)) + + // Change Source/Desination address and protocol + t.Run("Update_Existing_Rule", processSetRequest(ruleUrl, requestOneRulePatchJson, "PATCH", false)) + t.Run("Verify_One_Rule_Updation", processGetRequest(ruleUrl, responseOneRulePatchJson, false)) + + tcpFlagsUrl := ruleUrl + "/transport/config/tcp-flags" + t.Run("Delete_Tcp_Flags_Field", processDeleteRequest(tcpFlagsUrl)) + t.Run("Verify_Tcp_Flags_Deletion", processGetRequest(tcpFlagsUrl, emptyJson, false)) + + dscpUrl := ruleUrl + "/ipv4/config/dscp" + t.Run("Delete_IPv4_Dscp_Field", processDeleteRequest(dscpUrl)) + t.Run("Verify_IPv4_Dscp_Deletion", processGetRequest(dscpUrl, emptyRuleDscpJson, false)) + + protocolUrl := ruleUrl + "/ipv4/config/protocol" + t.Run("Delete_IPv4_Protocol_Field", processDeleteRequest(protocolUrl)) + t.Run("Verify_IPv4_Protocol_Deletion", processGetRequest(protocolUrl, emptyJson, false)) + + transportConfigUrl := ruleUrl + "/transport" + t.Run("Delete_Transport_Container", processDeleteRequest(transportConfigUrl)) + t.Run("Verify_Transport_Container_Deletion", processGetRequest(transportConfigUrl, emptyJson, false)) + + ipv4ConfigUrl := ruleUrl + "/ipv4/config" + t.Run("Delete_IPv4_Config_Container", processDeleteRequest(ipv4ConfigUrl)) + t.Run("Verify_IPv4_Config_Container_Deletion", processGetRequest(ipv4ConfigUrl, emptyJson, false)) + + t.Run("Delete_One_Rule", processDeleteRequest(ruleUrl)) + t.Run("Verify_One_Rule_Delete", processGetRequest(ruleUrl, "", true)) + + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +// This will test PUT (Replace) operation by Replacing multiple Rules with one Rule in an Acl +func Test_AclApp_ReplaceMultipleRulesWithOneRule(t *testing.T) { + url := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL3][type=ACL_IPV4]" + + t.Run("Create_One_Acl_With_Multiple_Rules(PATCH)", processSetRequest(url, oneAclCreateWithRulesJsonRequest, "PATCH", false)) + t.Run("Verify_Create_One_Acl_With_Multiple_Rules", processGetRequest(url, oneAclCreateWithRulesJsonResponse, false)) + + t.Run("Replace_All_Rules_With_One_Rule", processSetRequest(url, replaceMultiRulesWithOneRuleJsonRequest, "PUT", false)) + t.Run("Verify_Acl_With_Replaced_Rules", processGetRequest(url, replaceMultiRulesWithOneRuleJsonResponse, false)) + + t.Run("Delete_One_Acl_With_All_Its_Rules", processDeleteRequest(url)) + t.Run("Verify_One_Acl_Delete", processGetRequest(url, "", true)) +} + +// This will test PATCH operation by modifying Description of an Acl +func Test_AclApp_AclDescriptionUpdation(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]" + descrUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/config/description" + + t.Run("Create_One_Acl_Without_Rule", processSetRequest(aclUrl, oneAclCreateJsonRequest, "POST", false)) + + t.Run("Update_Description_of_Acl", processSetRequest(descrUrl, aclDescrUpdateJson, "PATCH", false)) + t.Run("Verify_Description_of_Acl", processGetRequest(descrUrl, aclDescrUpdateJson, false)) + + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +func Test_AclApp_AclIngressBindingOperations(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=8]" + bindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet4]/ingress-acl-sets/ingress-acl-set[set-name=MyACL5][type=ACL_IPV4]" + + t.Run("Create_One_Acl_Without_Rule", processSetRequest(aclUrl, oneAclCreateJsonRequest, "POST", false)) + + t.Run("Create_One_Rule", processSetRequest(ruleUrl, requestOneRulePostJson, "POST", false)) + + t.Run("Create_Ingress_Acl_set", processSetRequest(bindingUrl, ingressAclSetCreateJsonRequest, "POST", false)) + t.Run("Verify_Ingress_Aclset_Creation", processGetRequest(bindingUrl, ingressAclSetCreateJsonResponse, false)) + t.Run("Get_Port_Binding_From_Ingress_AclEntry_Level", processGetRequest(bindingUrl+"/acl-entries/acl-entry[sequence-id=8]", getBindingAclEntryResponse, false)) + + t.Run("Delete_Binding_From_Ingress_Aclset", processDeleteRequest(bindingUrl)) + t.Run("Verify_Binding_From_Ingress_Aclset_Deletion", processGetRequest(bindingUrl, "", true)) + t.Run("Delete_One_Rule", processDeleteRequest(ruleUrl)) + t.Run("Verify_One_Rule_Delete", processGetRequest(ruleUrl, "", true)) + + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +func Test_AclApp_AclEgressBindingOperations(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=8]" + bindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet4]/egress-acl-sets/egress-acl-set[set-name=MyACL5][type=ACL_IPV4]" + + t.Run("Create_One_Acl_Without_Rule", processSetRequest(aclUrl, oneAclCreateJsonRequest, "POST", false)) + + t.Run("Create_One_Rule", processSetRequest(ruleUrl, requestOneRulePostJson, "POST", false)) + + t.Run("Create_Egress_Acl_set", processSetRequest(bindingUrl, ingressAclSetCreateJsonRequest, "POST", false)) + t.Run("Verify_Egress_Aclset_Creation", processGetRequest(bindingUrl, egressAclSetCreateJsonResponse, false)) + t.Run("Get_Port_Binding_From_Egress_AclEntry_Level", processGetRequest(bindingUrl+"/acl-entries/acl-entry[sequence-id=8]", getBindingAclEntryResponse, false)) + + t.Run("Delete_Binding_From_Egress_Aclset", processDeleteRequest(bindingUrl)) + t.Run("Verify_Binding_From_Egress_Aclset_Deletion", processGetRequest(bindingUrl, "", true)) + t.Run("Delete_One_Rule", processDeleteRequest(ruleUrl)) + t.Run("Verify_One_Rule_Delete", processGetRequest(ruleUrl, "", true)) + + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +func Test_AclApp_GetOperationsFromMultipleTreeLevels(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=8]" + bindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet4]/egress-acl-sets/egress-acl-set[set-name=MyACL5][type=ACL_IPV4]" + + t.Run("Create_One_Acl_Without_Rule", processSetRequest(aclUrl, oneAclCreateJsonRequest, "POST", false)) + t.Run("Create_One_Rule", processSetRequest(ruleUrl, requestOneRulePostJson, "POST", false)) + t.Run("Create_Egress_Acl_set_Port_Binding", processSetRequest(bindingUrl, ingressAclSetCreateJsonRequest, "POST", false)) + + t.Run("Get_Acl_Tree_From_AclSets_level", processGetRequest("/openconfig-acl:acl/acl-sets", getFromAclSetsTreeLevelResponse, false)) + + t.Run("Get_All_Ports_Bindings_From_Interfaces_Tree_Level", processGetRequest("/openconfig-acl:acl/interfaces", getAllPortsFromInterfacesTreeLevelResponse, false)) + + t.Run("Get_One_Port_Binding_From_Interface_Tree_Level", processGetRequest("/openconfig-acl:acl/interfaces/interface[id=Ethernet4]", getPortBindingFromInterfaceTreeLevelResponse, false)) + + t.Run("Delete_Binding_From_Egress_Aclset", processDeleteRequest(bindingUrl)) + t.Run("Verify_Binding_From_Egress_Aclset_Deletion", processGetRequest(bindingUrl, "", true)) + + t.Run("Delete_One_Rule", processDeleteRequest(ruleUrl)) + t.Run("Verify_One_Rule_Delete", processGetRequest(ruleUrl, "", true)) + + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +func Test_AclApp_AddNewPortBindingToAlreadyBindedAcl(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=8]" + bindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet4]/egress-acl-sets/egress-acl-set[set-name=MyACL5][type=ACL_IPV4]" + + t.Run("Create_One_Acl_Without_Rule", processSetRequest(aclUrl, oneAclCreateJsonRequest, "POST", false)) + t.Run("Create_One_Rule", processSetRequest(ruleUrl, requestOneRulePostJson, "POST", false)) + t.Run("Create_Egress_Acl_set_Port_Binding", processSetRequest(bindingUrl, ingressAclSetCreateJsonRequest, "POST", false)) + + newBindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet0]/egress-acl-sets/egress-acl-set[set-name=MyACL5][type=ACL_IPV4]" + t.Run("Create_New_Egress_Acl_set_Port_Binding", processSetRequest(newBindingUrl, ingressAclSetCreateJsonRequest, "POST", false)) + + t.Run("Get_All_Ports_Bindings_From_Interfaces_Tree_Level", processGetRequest("/openconfig-acl:acl/interfaces", getMultiportBindingOnSingleAclResponse, false)) + + t.Run("Delete_All_Bindings_Top_Level", processDeleteRequest("/openconfig-acl:acl/interfaces")) + t.Run("Delete_All_Rules_Not_Acl", processDeleteRequest("/openconfig-acl:acl/acl-sets/acl-set[name=MyACL5][type=ACL_IPV4]/acl-entries")) + + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) + + t.Run("Verify_Top_Level_Delete", processGetRequest("/openconfig-acl:acl", emptyJson, false)) +} + +func Test_AclApp_IPv6AclAndRule(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL6][type=ACL_IPV6]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL6][type=ACL_IPV6]/acl-entries/acl-entry[sequence-id=6]" + bindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet4]/ingress-acl-sets/ingress-acl-set[set-name=MyACL6][type=ACL_IPV6]" + + t.Run("Create_One_IPv6_Acl_Without_Rule", processSetRequest(aclUrl, oneIPv6AclCreateJsonRequest, "POST", false)) + t.Run("Verify_One_IPv6_Acl_Without_Rule_Creation", processGetRequest(aclUrl, oneIPv6AclCreateJsonResponse, false)) + + t.Run("Create_One_IPv6_Rule", processSetRequest(ruleUrl, oneIPv6RuleCreateJsonRequest, "POST", false)) + t.Run("Verify_One_IPv6_Rule_Creation", processGetRequest(ruleUrl, oneIPv6RuleCreateJsonResponse, false)) + + t.Run("Create_Ingress_Acl_set", processSetRequest(bindingUrl, ingressIPv6AclSetCreateJsonRequest, "POST", false)) + t.Run("Verify_Ingress_Aclset_Creation", processGetRequest(bindingUrl, ingressIPv6AclSetCreateJsonResponse, false)) + + t.Run("Get_Acl_Tree_From_AclSet_level", processGetRequest("/openconfig-acl:acl/acl-sets/acl-set", getIPv6AclsFromAclSetListLevelResponse, false)) + t.Run("Get_All_Ports_Bindings_From_Interfaces_Tree_Level", processGetRequest("/openconfig-acl:acl/interfaces", getIPv6AllPortsBindingsResponse, false)) + + t.Run("Delete_Binding_From_Ingress_Aclset", processDeleteRequest(bindingUrl)) + t.Run("Verify_Binding_From_Ingress_Aclset_Deletion", processGetRequest(bindingUrl, "", true)) + + pktActionUrl := ruleUrl + "/actions/config/forwarding-action" + t.Run("Delete_Packet_Action_Field", processDeleteRequest(pktActionUrl)) + t.Run("Verify_Packet_Action_Field_Deletion", processGetRequest(pktActionUrl, emptyJson, false)) + + ipv6ConfigUrl := ruleUrl + "/ipv6/config" + t.Run("Delete_IPv6_Config", processDeleteRequest(ipv6ConfigUrl)) + t.Run("Verify_IPv6_Config_Deletion", processGetRequest(ipv6ConfigUrl, emptyJson, false)) + + t.Run("Delete_One_Rule", processDeleteRequest(ruleUrl)) + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +func Test_AclApp_L2AclAndRule(t *testing.T) { + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL2][type=ACL_L2]" + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL2][type=ACL_L2]/acl-entries/acl-entry[sequence-id=2]" + bindingUrl := "/openconfig-acl:acl/interfaces/interface[id=Ethernet0]/ingress-acl-sets/ingress-acl-set[set-name=MyACL2][type=ACL_L2]" + + t.Run("Create_One_L2_Acl_Without_Rule", processSetRequest(aclUrl, oneL2AclCreateJsonRequest, "POST", false)) + t.Run("Verify_One_L2_Acl_Without_Rule_Creation", processGetRequest(aclUrl, oneL2AclCreateJsonResponse, false)) + + t.Run("Create_One_L2_Rule", processSetRequest(ruleUrl, oneL2RuleCreateJsonRequest, "POST", false)) + t.Run("Verify_One_L2_Rule_Creation", processGetRequest(ruleUrl, oneL2RuleCreateJsonResponse, false)) + + t.Run("Create_Ingress_L2_Acl_set", processSetRequest(bindingUrl, ingressL2AclSetCreateJsonRequest, "POST", false)) + t.Run("Verify_Ingress_L2_Aclset_Creation", processGetRequest(bindingUrl, ingressL2AclSetCreateJsonResponse, false)) + + t.Run("Get_Acl_Tree_From_AclSet_level", processGetRequest("/openconfig-acl:acl/acl-sets/acl-set", getL2AclsFromAclSetListLevelResponse, false)) + t.Run("Get_All_Ports_Bindings_From_Interfaces_Tree_Level", processGetRequest("/openconfig-acl:acl/interfaces", getL2AllPortsBindingsResponse, false)) + + t.Run("Delete_Binding_From_Ingress_Aclset", processDeleteRequest(bindingUrl)) + t.Run("Verify_Binding_From_Ingress_Aclset_Deletion", processGetRequest(bindingUrl, "", true)) + + etherTypeUrl := ruleUrl + "/l2/config/ethertype" + t.Run("Delete_Ethertype_Field", processDeleteRequest(etherTypeUrl)) + t.Run("Verify_L2_Ethertype_Field_Deletion", processGetRequest(ruleUrl+"/l2/config", emptyJson, false)) + + t.Run("Delete_Transport_Src_Port_Field", processDeleteRequest(ruleUrl+"/transport/config/source-port")) + t.Run("Delete_Transport_Dst_Port_Field", processDeleteRequest(ruleUrl+"/transport/config/destination-port")) + t.Run("Delete_Transport_Tcp_Flags_Field", processDeleteRequest(ruleUrl+"/transport/config/tcp-flags")) + t.Run("Verify_Transport_Src_Dst_Fields_Deletion", processGetRequest(ruleUrl+"/transport/config", emptyJson, false)) + + t.Run("Delete_One_Rule", processDeleteRequest(ruleUrl)) + t.Run("Delete_One_Acl", processDeleteRequest(aclUrl)) + t.Run("Verify_One_Acl_Delete", processGetRequest(aclUrl, "", true)) +} + +func Test_AclApp_NegativeTests(t *testing.T) { + // Verify GET returns errors for non-existing ACL + aclUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL2][type=ACL_L2]" + t.Run("Verify_Non_Existing_Acl_GET_Error", processGetRequest(aclUrl, "", true)) + + // Verify GET returns errors for non-existing Rule + ruleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL2][type=ACL_L2]/acl-entries/acl-entry[sequence-id=2]" + t.Run("Verify_Non_Existing_Rule_GET_Error", processGetRequest(ruleUrl, "", true)) + + // Verify Error on giving Invalid Interface in payload during binding creation + url := "/openconfig-acl:acl" + t.Run("Create_Acl_With_Invalid_Interface_Binding", processSetRequest(url, aclCreateWithInvalidInterfaceBinding, "POST", true)) + + // Verify error if duplicate Acl is created using POST + t.Run("Create_One_L2_Acl_Without_Rule", processSetRequest(aclUrl, oneL2AclCreateJsonRequest, "POST", false)) + t.Run("Verify_One_L2_Acl_Without_Rule_Creation", processGetRequest(aclUrl, oneL2AclCreateJsonResponse, false)) + t.Run("Verify_Error_On_Create_Duplicate_L2_Acl", processSetRequest(aclUrl, oneL2AclCreateJsonRequest, "POST", true)) + + // Verify error if duplicate Rule is created using POST + multiRuleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL3][type=ACL_IPV4]" + t.Run("Create_One_Acl_With_Multiple_Rules(PATCH)", processSetRequest(multiRuleUrl, oneAclCreateWithRulesJsonRequest, "PATCH", false)) + t.Run("Verify_Create_One_Acl_With_Multiple_Rules", processGetRequest(multiRuleUrl, oneAclCreateWithRulesJsonResponse, true)) + + duplicateRuleUrl := "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL3][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=1]" + t.Run("Create_One_Duplicate_Rule", processSetRequest(duplicateRuleUrl, requestOneDuplicateRulePostJson, "POST", true)) + + topLevelUrl := "/openconfig-acl:acl" + t.Run("Delete_Full_ACl_Tree_Top_Level", processDeleteRequest(topLevelUrl)) + t.Run("Verify_Top_Level_Delete", processGetRequest(topLevelUrl, emptyJson, false)) +} + + +// THis will delete ACL table and Rules Table from DB +func clearAclDataFromDb() error { + var err error + ruleTable := db.TableSpec{Name: "ACL_RULE"} + aclTable := db.TableSpec{Name: "ACL_TABLE"} + + d := getConfigDb() + if d == nil { + err = errors.New("Failed to connect to config Db") + return err + } + if err = d.DeleteTable(&ruleTable); err != nil { + err = errors.New("Failed to delete Rules Table") + return err + } + if err = d.DeleteTable(&aclTable); err != nil { + err = errors.New("Failed to delete Acl Table") + return err + } + return err +} + + +func Test_AclApp_Subscribe(t *testing.T) { + app := new(AclApp) + + t.Run("top", testSubsError(app, "/")) + t.Run("unknown", testSubsError(app, "/some/unknown/path")) + t.Run("topacl", testSubsError(app, "/openconfig-acl:acl")) + t.Run("aclsets", testSubsError(app, "/openconfig-acl:acl/acl-sets")) + t.Run("aclset*", testSubsError(app, "/openconfig-acl:acl/acl-sets/acl-set")) + t.Run("aclset", testSubsError(app, "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]")) + + t.Run("acl_config", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/config/description", + "ACL_TABLE", "X_ACL_IPV4", true)) + + t.Run("acl_state", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/state", + "ACL_TABLE", "X_ACL_IPV4", true)) + + t.Run("entries", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/acl-entries", + "ACL_RULE", "X_ACL_IPV4|*", false)) + + t.Run("rule*", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/acl-entries/acl-entry", + "ACL_RULE", "X_ACL_IPV4|*", false)) + + t.Run("rule", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=1]", + "ACL_RULE", "X_ACL_IPV4|RULE_1", false)) + + t.Run("rule_state", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=100]/state", + "ACL_RULE", "X_ACL_IPV4|RULE_100", true)) + + t.Run("rule_sip", testSubs(app, + "/openconfig-acl:acl/acl-sets/acl-set[name=X][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=200]/ipv4/config/source-address", + "ACL_RULE", "X_ACL_IPV4|RULE_200", true)) + +} + +// testSubs creates a test case which invokes translateSubscribe on an +// app interafce and check returned notificationInfo matches given values. +func testSubs(app appInterface, path, oTable, oKey string, oCache bool) func(*testing.T) { + return func(t *testing.T) { + _, nt, err := app.translateSubscribe([db.MaxDB]*db.DB{}, path) + if err != nil { + t.Fatalf("Unexpected error processing '%s'; err=%v", path, err) + } + if nt == nil || nt.needCache != oCache || nt.table.Name != oTable || + !reflect.DeepEqual(nt.key.Comp, strings.Split(oKey, "|")) { + t.Logf("translateSubscribe for path '%s'", path) + t.Logf("Expected table '%s', key '%v', cache %v", oTable, oKey, oCache) + if nt == nil { + t.Fatalf("Found nil") + } else { + t.Fatalf("Found table '%s', key '%s', cache %v", + nt.table.Name, strings.Join(nt.key.Comp, "|"), nt.needCache) + } + } + } +} + +// testSubsError creates a test case which invokes translateSubscribe on +// an app interafce and expects it to return an error +func testSubsError(app appInterface, path string) func(*testing.T) { + return func(t *testing.T) { + _, _, err := app.translateSubscribe([db.MaxDB]*db.DB{}, path) + if err == nil { + t.Fatalf("Expected error for path '%s'", path) + } + } +} + +/***************************************************************************/ +/////////// JSON Data for Tests /////////////// +/***************************************************************************/ + +var bulkAclCreateJsonRequest string = "{\"acl-sets\":{\"acl-set\":[{\"name\":\"MyACL1\",\"type\":\"ACL_IPV4\",\"config\":{\"name\":\"MyACL1\",\"type\":\"ACL_IPV4\",\"description\":\"Description for MyACL1\"},\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":1,\"config\":{\"sequence-id\":1,\"description\":\"Description for MyACL1 Rule Seq 1\"},\"ipv4\":{\"config\":{\"source-address\":\"11.1.1.1/32\",\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":101,\"destination-port\":201}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}},{\"sequence-id\":2,\"config\":{\"sequence-id\":2,\"description\":\"Description for MyACL1 Rule Seq 2\"},\"ipv4\":{\"config\":{\"source-address\":\"11.1.1.2/32\",\"destination-address\":\"21.1.1.2/32\",\"dscp\":2,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":102,\"destination-port\":202}},\"actions\":{\"config\":{\"forwarding-action\":\"DROP\"}}},{\"sequence-id\":3,\"config\":{\"sequence-id\":3,\"description\":\"Description for MyACL1 Rule Seq 3\"},\"ipv4\":{\"config\":{\"source-address\":\"11.1.1.3/32\",\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":103,\"destination-port\":203}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}},{\"sequence-id\":4,\"config\":{\"sequence-id\":4,\"description\":\"Description for MyACL1 Rule Seq 4\"},\"ipv4\":{\"config\":{\"source-address\":\"11.1.1.4/32\",\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":104,\"destination-port\":204}},\"actions\":{\"config\":{\"forwarding-action\":\"DROP\"}}},{\"sequence-id\":5,\"config\":{\"sequence-id\":5,\"description\":\"Description for MyACL1 Rule Seq 5\"},\"ipv4\":{\"config\":{\"source-address\":\"11.1.1.5/32\",\"destination-address\":\"21.1.1.5/32\",\"dscp\":5,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":105,\"destination-port\":205}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}}]}},{\"name\":\"MyACL2\",\"type\":\"ACL_IPV4\",\"config\":{\"name\":\"MyACL2\",\"type\":\"ACL_IPV4\",\"description\":\"Description for MyACL2\"},\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":1,\"config\":{\"sequence-id\":1,\"description\":\"Description for Rule Seq 1\"},\"ipv4\":{\"config\":{\"source-address\":\"12.1.1.1/32\",\"destination-address\":\"22.1.1.1/32\",\"dscp\":1,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":101,\"destination-port\":201}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}},{\"sequence-id\":2,\"config\":{\"sequence-id\":2,\"description\":\"Description for Rule Seq 2\"},\"ipv4\":{\"config\":{\"source-address\":\"12.1.1.2/32\",\"destination-address\":\"22.1.1.2/32\",\"dscp\":2,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":102,\"destination-port\":202}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}},{\"sequence-id\":3,\"config\":{\"sequence-id\":3,\"description\":\"Description for Rule Seq 3\"},\"ipv4\":{\"config\":{\"source-address\":\"12.1.1.3/32\",\"destination-address\":\"22.1.1.3/32\",\"dscp\":3,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":103,\"destination-port\":203}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}},{\"sequence-id\":4,\"config\":{\"sequence-id\":4,\"description\":\"Description for Rule Seq 4\"},\"ipv4\":{\"config\":{\"source-address\":\"12.1.1.4/32\",\"destination-address\":\"22.1.1.4/32\",\"dscp\":4,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":104,\"destination-port\":204}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}},{\"sequence-id\":5,\"config\":{\"sequence-id\":5,\"description\":\"Description for Rule Seq 5\"},\"ipv4\":{\"config\":{\"source-address\":\"12.1.1.5/32\",\"destination-address\":\"22.1.1.5/32\",\"dscp\":5,\"protocol\":\"IP_TCP\"}},\"transport\":{\"config\":{\"source-port\":105,\"destination-port\":205}},\"actions\":{\"config\":{\"forwarding-action\":\"ACCEPT\"}}}]}}]},\"interfaces\":{\"interface\":[{\"id\":\"Ethernet0\",\"config\":{\"id\":\"Ethernet0\"},\"interface-ref\":{\"config\":{\"interface\":\"Ethernet0\"}},\"ingress-acl-sets\":{\"ingress-acl-set\":[{\"set-name\":\"MyACL1\",\"type\":\"ACL_IPV4\",\"config\":{\"set-name\":\"MyACL1\",\"type\":\"ACL_IPV4\"}}]}},{\"id\":\"Ethernet4\",\"config\":{\"id\":\"Ethernet4\"},\"interface-ref\":{\"config\":{\"interface\":\"Ethernet4\"}},\"ingress-acl-sets\":{\"ingress-acl-set\":[{\"set-name\":\"MyACL2\",\"type\":\"ACL_IPV4\",\"config\":{\"set-name\":\"MyACL2\",\"type\":\"ACL_IPV4\"}}]}}]}}" + +var bulkAclCreateJsonResponse string = "{\"openconfig-acl:acl\":{\"acl-sets\":{\"acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":1},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.1/32\"},\"state\":{\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.1/32\"}},\"sequence-id\":1,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":1},\"transport\":{\"config\":{\"destination-port\":201,\"source-port\":101},\"state\":{\"destination-port\":201,\"source-port\":101}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"},\"state\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":2},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.2/32\",\"dscp\":2,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.2/32\"},\"state\":{\"destination-address\":\"21.1.1.2/32\",\"dscp\":2,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.2/32\"}},\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2},\"transport\":{\"config\":{\"destination-port\":202,\"source-port\":102},\"state\":{\"destination-port\":202,\"source-port\":102}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":3},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.3/32\"},\"state\":{\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.3/32\"}},\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3},\"transport\":{\"config\":{\"destination-port\":203,\"source-port\":103},\"state\":{\"destination-port\":203,\"source-port\":103}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"},\"state\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":4},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.4/32\"},\"state\":{\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.4/32\"}},\"sequence-id\":4,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":4},\"transport\":{\"config\":{\"destination-port\":204,\"source-port\":104},\"state\":{\"destination-port\":204,\"source-port\":104}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":5},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.5/32\"},\"state\":{\"destination-address\":\"21.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.5/32\"}},\"sequence-id\":5,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":5},\"transport\":{\"config\":{\"destination-port\":205,\"source-port\":105},\"state\":{\"destination-port\":205,\"source-port\":105}}}]},\"config\":{\"description\":\"Description for MyACL1\",\"name\":\"MyACL1\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL1\",\"state\":{\"description\":\"Description for MyACL1\",\"name\":\"MyACL1\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"},{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":1},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.1/32\"},\"state\":{\"destination-address\":\"22.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.1/32\"}},\"sequence-id\":1,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":1},\"transport\":{\"config\":{\"destination-port\":201,\"source-port\":101},\"state\":{\"destination-port\":201,\"source-port\":101}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":2},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.2/32\",\"dscp\":2,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.2/32\"},\"state\":{\"destination-address\":\"22.1.1.2/32\",\"dscp\":2,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.2/32\"}},\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2},\"transport\":{\"config\":{\"destination-port\":202,\"source-port\":102},\"state\":{\"destination-port\":202,\"source-port\":102}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":3},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.3/32\"},\"state\":{\"destination-address\":\"22.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.3/32\"}},\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3},\"transport\":{\"config\":{\"destination-port\":203,\"source-port\":103},\"state\":{\"destination-port\":203,\"source-port\":103}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":4},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.4/32\"},\"state\":{\"destination-address\":\"22.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.4/32\"}},\"sequence-id\":4,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":4},\"transport\":{\"config\":{\"destination-port\":204,\"source-port\":104},\"state\":{\"destination-port\":204,\"source-port\":104}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":5},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.5/32\"},\"state\":{\"destination-address\":\"22.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.5/32\"}},\"sequence-id\":5,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":5},\"transport\":{\"config\":{\"destination-port\":205,\"source-port\":105},\"state\":{\"destination-port\":205,\"source-port\":105}}}]},\"config\":{\"description\":\"Description for MyACL2\",\"name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL2\",\"state\":{\"description\":\"Description for MyACL2\",\"name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"interfaces\":{\"interface\":[{\"config\":{\"id\":\"Ethernet0\"},\"id\":\"Ethernet0\",\"ingress-acl-sets\":{\"ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":1,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":1}},{\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2}},{\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3}},{\"sequence-id\":4,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":4}},{\"sequence-id\":5,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":5}}]},\"config\":{\"set-name\":\"MyACL1\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL1\",\"state\":{\"set-name\":\"MyACL1\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"state\":{\"id\":\"Ethernet0\"}},{\"config\":{\"id\":\"Ethernet4\"},\"id\":\"Ethernet4\",\"ingress-acl-sets\":{\"ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":1,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":1}},{\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2}},{\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3}},{\"sequence-id\":4,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":4}},{\"sequence-id\":5,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":5}}]},\"config\":{\"set-name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL2\",\"state\":{\"set-name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"state\":{\"id\":\"Ethernet4\"}}]}}}" + +var oneAclCreateWithRulesJsonRequest string = "{ \"openconfig-acl:acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" }, \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": 1, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL3 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } }, { \"sequence-id\": 2, \"config\": { \"sequence-id\": 2, \"description\": \"Description for MyACL3 Rule Seq 2\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.2/32\", \"destination-address\": \"21.1.1.2/32\", \"dscp\": 2, \"protocol\": \"IP_UDP\" } }, \"transport\": { \"config\": { \"source-port\": 102, \"destination-port\": 202 } }, \"actions\": { \"config\": { \"forwarding-action\": \"DROP\" } } }, { \"sequence-id\": 3, \"config\": { \"sequence-id\": 3, \"description\": \"Description for MyACL3 Rule Seq 3\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.3/32\", \"destination-address\": \"21.1.1.3/32\", \"dscp\": 3, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 103, \"destination-port\": 203 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } }, { \"sequence-id\": 4, \"config\": { \"sequence-id\": 4, \"description\": \"Description for MyACL3 Rule Seq 4\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.4/32\", \"destination-address\": \"21.1.1.4/32\", \"dscp\": 4, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 104, \"destination-port\": 204 } }, \"actions\": { \"config\": { \"forwarding-action\": \"DROP\" } } }, { \"sequence-id\": 5, \"config\": { \"sequence-id\": 5, \"description\": \"Description for MyACL3 Rule Seq 5\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.5/32\", \"destination-address\": \"21.1.1.5/32\", \"dscp\": 5, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 105, \"destination-port\": 205 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] }} ] }" + +var oneAclCreateWithRulesJsonResponse string = "{\"openconfig-acl:acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":1},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.1/32\"},\"state\":{\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.1/32\"}},\"sequence-id\":1,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":1},\"transport\":{\"config\":{\"destination-port\":201,\"source-port\":101},\"state\":{\"destination-port\":201,\"source-port\":101}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"},\"state\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":2},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.2/32\",\"dscp\":2,\"protocol\":\"openconfig-packet-match-types:IP_UDP\",\"source-address\":\"11.1.1.2/32\"},\"state\":{\"destination-address\":\"21.1.1.2/32\",\"dscp\":2,\"protocol\":\"openconfig-packet-match-types:IP_UDP\",\"source-address\":\"11.1.1.2/32\"}},\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2},\"transport\":{\"config\":{\"destination-port\":202,\"source-port\":102},\"state\":{\"destination-port\":202,\"source-port\":102}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":3},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.3/32\"},\"state\":{\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.3/32\"}},\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3},\"transport\":{\"config\":{\"destination-port\":203,\"source-port\":103},\"state\":{\"destination-port\":203,\"source-port\":103}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"},\"state\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":4},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.4/32\"},\"state\":{\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.4/32\"}},\"sequence-id\":4,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":4},\"transport\":{\"config\":{\"destination-port\":204,\"source-port\":104},\"state\":{\"destination-port\":204,\"source-port\":104}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":5},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.5/32\"},\"state\":{\"destination-address\":\"21.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.5/32\"}},\"sequence-id\":5,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":5},\"transport\":{\"config\":{\"destination-port\":205,\"source-port\":105},\"state\":{\"destination-port\":205,\"source-port\":105}}}]},\"config\":{\"description\":\"Description for MyACL3\",\"name\":\"MyACL3\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL3\",\"state\":{\"description\":\"Description for MyACL3\",\"name\":\"MyACL3\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]}" + +var oneAclCreateJsonRequest string = "{\"config\": {\"name\": \"MyACL5\",\"type\": \"ACL_IPV4\",\"description\": \"Description for MyACL5\"}}" +var oneAclCreateJsonResponse string = "{\"openconfig-acl:acl-set\":[{\"config\":{\"description\":\"Description for MyACL5\",\"name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL5\",\"state\":{\"description\":\"Description for MyACL5\",\"name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]}" + +var aclDescrUpdateJson string = "{\"openconfig-acl:description\":\"Verifying ACL Description Update\"}" + +var requestOneRulePostJson string = "{\"sequence-id\": 8,\"config\": {\"sequence-id\": 8,\"description\": \"Description for MyACL5 Rule Seq 8\"},\"ipv4\": {\"config\": {\"source-address\": \"4.4.4.4/24\",\"destination-address\": \"5.5.5.5/24\",\"protocol\": \"IP_TCP\"}},\"transport\": {\"config\": {\"source-port\": 101,\"destination-port\": 100,\"tcp-flags\": [\"TCP_FIN\",\"TCP_ACK\"]}},\"actions\": {\"config\": {\"forwarding-action\": \"ACCEPT\"}}}" + +var requestOneRulePatchJson string = "{ \"openconfig-acl:acl-entry\": [ {\"sequence-id\": 8,\"config\": {\"sequence-id\": 8,\"description\": \"Description for MyACL5 Rule Seq 8\"},\"ipv4\": {\"config\": {\"source-address\": \"4.8.4.8/24\",\"destination-address\": \"15.5.15.5/24\",\"protocol\": \"IP_L2TP\"}},\"transport\": {\"config\": {\"source-port\": 101,\"destination-port\": 100,\"tcp-flags\": [\"TCP_FIN\",\"TCP_ACK\",\"TCP_RST\",\"TCP_ECE\"]}},\"actions\": {\"config\": {\"forwarding-action\": \"ACCEPT\"}}} ] }" + +var responseOneRuleJson string = "{\"openconfig-acl:acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":8},\"ipv4\":{\"config\":{\"destination-address\":\"5.5.5.5/24\",\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"4.4.4.4/24\"},\"state\":{\"destination-address\":\"5.5.5.5/24\",\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"4.4.4.4/24\"}},\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]}}}]}" + +var responseOneRulePatchJson string = "{\"openconfig-acl:acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":8},\"ipv4\":{\"config\":{\"destination-address\":\"15.5.15.5/24\",\"protocol\":\"openconfig-packet-match-types:IP_L2TP\",\"source-address\":\"4.8.4.8/24\"},\"state\":{\"destination-address\":\"15.5.15.5/24\",\"protocol\":\"openconfig-packet-match-types:IP_L2TP\",\"source-address\":\"4.8.4.8/24\"}},\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_RST\",\"openconfig-packet-match-types:TCP_ACK\",\"openconfig-packet-match-types:TCP_ECE\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_RST\",\"openconfig-packet-match-types:TCP_ACK\",\"openconfig-packet-match-types:TCP_ECE\"]}}}]}" + +var emptyAclDescriptionJson string = "{\"openconfig-acl:description\":\"\"}" +var emptyRuleDscpJson string = "{\"openconfig-acl:dscp\":0}" + +var ingressAclSetCreateJsonRequest string = "{ \"openconfig-acl:config\": { \"set-name\": \"MyACL5\", \"type\": \"ACL_IPV4\" }}" +var ingressAclSetCreateJsonResponse string = "{\"openconfig-acl:ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]},\"config\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL5\",\"state\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]}" + +var egressAclSetCreateJsonResponse string = "{\"openconfig-acl:egress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]},\"config\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL5\",\"state\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]}" + +var replaceMultiRulesWithOneRuleJsonRequest string = "{ \"openconfig-acl:acl-set\": [ {\"name\": \"MyACL3\",\"type\": \"ACL_IPV4\",\"config\": {\"name\": \"MyACL3\",\"type\": \"ACL_IPV4\",\"description\": \"Description for MyACL3\"},\"acl-entries\": {\"acl-entry\": [{\"sequence-id\": 8,\"config\": {\"sequence-id\": 8,\"description\": \"Description for MyACL3 Rule Seq 8\"},\"ipv4\": {\"config\": {\"source-address\": \"81.1.1.1/32\",\"destination-address\": \"91.1.1.1/32\",\"protocol\": \"IP_TCP\"}},\"transport\": {\"config\": {\"source-port\": \"801..811\",\"destination-port\": \"901..921\"}},\"actions\": {\"config\": {\"forwarding-action\": \"REJECT\"}}}]}} ] }" + +var replaceMultiRulesWithOneRuleJsonResponse string = "{\"openconfig-acl:acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"},\"state\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":8},\"ipv4\":{\"config\":{\"destination-address\":\"91.1.1.1/32\",\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"81.1.1.1/32\"},\"state\":{\"destination-address\":\"91.1.1.1/32\",\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"81.1.1.1/32\"}},\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8},\"transport\":{\"config\":{\"destination-port\":\"901-921\",\"source-port\":\"801-811\"},\"state\":{\"destination-port\":\"901-921\",\"source-port\":\"801-811\"}}}]},\"config\":{\"description\":\"Description for MyACL3\",\"name\":\"MyACL3\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL3\",\"state\":{\"description\":\"Description for MyACL3\",\"name\":\"MyACL3\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]}" + +var getFromAclSetsTreeLevelResponse string = "{\"openconfig-acl:acl-sets\":{\"acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":8},\"ipv4\":{\"config\":{\"destination-address\":\"5.5.5.5/24\",\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"4.4.4.4/24\"},\"state\":{\"destination-address\":\"5.5.5.5/24\",\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"4.4.4.4/24\"}},\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]}}}]},\"config\":{\"description\":\"Description for MyACL5\",\"name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL5\",\"state\":{\"description\":\"Description for MyACL5\",\"name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]}}" + +var getAllPortsFromInterfacesTreeLevelResponse string = "{\"openconfig-acl:interfaces\":{\"interface\":[{\"config\":{\"id\":\"Ethernet4\"},\"egress-acl-sets\":{\"egress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]},\"config\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL5\",\"state\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"id\":\"Ethernet4\",\"state\":{\"id\":\"Ethernet4\"}}]}}" + +var getPortBindingFromInterfaceTreeLevelResponse string = "{\"openconfig-acl:interface\":[{\"config\":{\"id\":\"Ethernet4\"},\"egress-acl-sets\":{\"egress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]},\"config\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL5\",\"state\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"id\":\"Ethernet4\",\"state\":{\"id\":\"Ethernet4\"}}]}" + +var getBindingAclEntryResponse string = "{\"openconfig-acl:acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]}" + +var getMultiportBindingOnSingleAclResponse string = "{\"openconfig-acl:interfaces\":{\"interface\":[{\"config\":{\"id\":\"Ethernet0\"},\"egress-acl-sets\":{\"egress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]},\"config\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL5\",\"state\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"id\":\"Ethernet0\",\"state\":{\"id\":\"Ethernet0\"}},{\"config\":{\"id\":\"Ethernet4\"},\"egress-acl-sets\":{\"egress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":8,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":8}}]},\"config\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"set-name\":\"MyACL5\",\"state\":{\"set-name\":\"MyACL5\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]},\"id\":\"Ethernet4\",\"state\":{\"id\":\"Ethernet4\"}}]}}" + +var oneIPv6AclCreateJsonRequest string = "{\"config\": {\"name\": \"MyACL6\",\"type\": \"ACL_IPV6\",\"description\": \"Description for IPv6 ACL MyACL6\"}}" +var oneIPv6AclCreateJsonResponse string = "{\"openconfig-acl:acl-set\":[{\"config\":{\"description\":\"Description for IPv6 ACL MyACL6\",\"name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"name\":\"MyACL6\",\"state\":{\"description\":\"Description for IPv6 ACL MyACL6\",\"name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"type\":\"openconfig-acl:ACL_IPV6\"}]}" + +var oneIPv6RuleCreateJsonRequest string = "{\"sequence-id\": 6,\"config\": {\"sequence-id\": 6,\"description\": \"Description for MyACL6 Rule Seq 6\"},\"ipv6\": {\"config\": {\"source-address\": \"11::67/64\",\"destination-address\": \"22::87/64\",\"protocol\": \"IP_TCP\",\"dscp\": 11}},\"transport\": {\"config\": {\"source-port\": 101,\"destination-port\": 100,\"tcp-flags\": [\"TCP_FIN\",\"TCP_ACK\"]}},\"actions\": {\"config\": {\"forwarding-action\": \"ACCEPT\"}}}" +var oneIPv6RuleCreateJsonResponse string = "{\"openconfig-acl:acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":6},\"ipv6\":{\"config\":{\"destination-address\":\"22::87/64\",\"dscp\":11,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11::67/64\"},\"state\":{\"destination-address\":\"22::87/64\",\"dscp\":11,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11::67/64\"}},\"sequence-id\":6,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":6},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]}}}]}" + +var ingressIPv6AclSetCreateJsonRequest string = "{ \"openconfig-acl:config\": { \"set-name\": \"MyACL6\", \"type\": \"ACL_IPV6\" }}" +var ingressIPv6AclSetCreateJsonResponse string = "{\"openconfig-acl:ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":6,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":6}}]},\"config\":{\"set-name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"set-name\":\"MyACL6\",\"state\":{\"set-name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"type\":\"openconfig-acl:ACL_IPV6\"}]}" + +var getIPv6AclsFromAclSetListLevelResponse string = "{\"openconfig-acl:acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":6},\"ipv6\":{\"config\":{\"destination-address\":\"22::87/64\",\"dscp\":11,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11::67/64\"},\"state\":{\"destination-address\":\"22::87/64\",\"dscp\":11,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11::67/64\"}},\"sequence-id\":6,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":6},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]}}}]},\"config\":{\"description\":\"Description for IPv6 ACL MyACL6\",\"name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"name\":\"MyACL6\",\"state\":{\"description\":\"Description for IPv6 ACL MyACL6\",\"name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"type\":\"openconfig-acl:ACL_IPV6\"}]}" + +var getIPv6AllPortsBindingsResponse string = "{\"openconfig-acl:interfaces\":{\"interface\":[{\"config\":{\"id\":\"Ethernet4\"},\"id\":\"Ethernet4\",\"ingress-acl-sets\":{\"ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":6,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":6}}]},\"config\":{\"set-name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"set-name\":\"MyACL6\",\"state\":{\"set-name\":\"MyACL6\",\"type\":\"openconfig-acl:ACL_IPV6\"},\"type\":\"openconfig-acl:ACL_IPV6\"}]},\"state\":{\"id\":\"Ethernet4\"}}]}}" + +var oneL2AclCreateJsonRequest string = "{\"config\": {\"name\": \"MyACL2\",\"type\": \"ACL_L2\",\"description\": \"Description for L2 ACL MyACL2\"}}" +var oneL2AclCreateJsonResponse string = "{\"openconfig-acl:acl-set\":[{\"config\":{\"description\":\"Description for L2 ACL MyACL2\",\"name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"name\":\"MyACL2\",\"state\":{\"description\":\"Description for L2 ACL MyACL2\",\"name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"type\":\"openconfig-acl:ACL_L2\"}]}" + +var oneL2RuleCreateJsonRequest string = "{\"sequence-id\": 2,\"config\": {\"sequence-id\": 2,\"description\": \"Description for MyACL2 Rule Seq 2\"},\"l2\": {\"config\": {\"ethertype\": \"ETHERTYPE_VLAN\"}},\"transport\": {\"config\": {\"source-port\": 101,\"destination-port\": 100,\"tcp-flags\": [\"TCP_FIN\",\"TCP_ACK\"]}},\"actions\": {\"config\": {\"forwarding-action\": \"ACCEPT\"}}}" + +var oneL2RuleCreateJsonResponse string = "{\"openconfig-acl:acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":2},\"l2\":{\"config\":{\"ethertype\":\"openconfig-packet-match-types:ETHERTYPE_VLAN\"},\"state\":{\"ethertype\":\"openconfig-packet-match-types:ETHERTYPE_VLAN\"}},\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]}}}]}" + +var ingressL2AclSetCreateJsonRequest string = "{ \"openconfig-acl:config\": { \"set-name\": \"MyACL2\", \"type\": \"ACL_L2\" }}" +var ingressL2AclSetCreateJsonResponse string = "{\"openconfig-acl:ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2}}]},\"config\":{\"set-name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"set-name\":\"MyACL2\",\"state\":{\"set-name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"type\":\"openconfig-acl:ACL_L2\"}]}" + +var getL2AclsFromAclSetListLevelResponse string = "{\"openconfig-acl:acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"},\"state\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":2},\"l2\":{\"config\":{\"ethertype\":\"openconfig-packet-match-types:ETHERTYPE_VLAN\"},\"state\":{\"ethertype\":\"openconfig-packet-match-types:ETHERTYPE_VLAN\"}},\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2},\"transport\":{\"config\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]},\"state\":{\"destination-port\":100,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\",\"openconfig-packet-match-types:TCP_ACK\"]}}}]},\"config\":{\"description\":\"Description for L2 ACL MyACL2\",\"name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"name\":\"MyACL2\",\"state\":{\"description\":\"Description for L2 ACL MyACL2\",\"name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"type\":\"openconfig-acl:ACL_L2\"}]}" + +var getL2AllPortsBindingsResponse string = "{\"openconfig-acl:interfaces\":{\"interface\":[{\"config\":{\"id\":\"Ethernet0\"},\"id\":\"Ethernet0\",\"ingress-acl-sets\":{\"ingress-acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2}}]},\"config\":{\"set-name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"set-name\":\"MyACL2\",\"state\":{\"set-name\":\"MyACL2\",\"type\":\"openconfig-acl:ACL_L2\"},\"type\":\"openconfig-acl:ACL_L2\"}]},\"state\":{\"id\":\"Ethernet0\"}}]}}" + +var aclCreateWithInvalidInterfaceBinding string = "{ \"acl-sets\": { \"acl-set\": [ { \"name\": \"MyACL1\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL1\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL1\" }, \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": 1, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL1 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] } } ] }, \"interfaces\": { \"interface\": [ { \"id\": \"Ethernet2112\", \"config\": { \"id\": \"Ethernet2112\" }, \"interface-ref\": { \"config\": { \"interface\": \"Ethernet2112\" } }, \"ingress-acl-sets\": { \"ingress-acl-set\": [ { \"set-name\": \"MyACL1\", \"type\": \"ACL_IPV4\", \"config\": { \"set-name\": \"MyACL1\", \"type\": \"ACL_IPV4\" } } ] } } ] }}" + +var requestOneDuplicateRulePostJson string = "{\"sequence-id\": 1,\"config\": {\"sequence-id\": 1,\"description\": \"Description for MyACL3 Rule Seq 1\"},\"ipv4\": {\"config\": {\"source-address\": \"4.4.4.4/24\",\"destination-address\": \"5.5.5.5/24\",\"protocol\": \"IP_TCP\"}},\"transport\": {\"config\": {\"source-port\": 101,\"destination-port\": 100,\"tcp-flags\": [\"TCP_FIN\",\"TCP_ACK\"]}},\"actions\": {\"config\": {\"forwarding-action\": \"ACCEPT\"}}}" diff --git a/src/translib/api_tests_app.go b/src/translib/api_tests_app.go new file mode 100644 index 0000000000..17e3771e02 --- /dev/null +++ b/src/translib/api_tests_app.go @@ -0,0 +1,162 @@ +/////////////////////////////////////////////////////////////////////// +// +// Copyright 2019 Broadcom. All rights reserved. +// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. +// +/////////////////////////////////////////////////////////////////////// + +package translib + +import ( + "encoding/json" + "reflect" + "strings" + "translib/db" + "translib/tlerr" + + "github.com/golang/glog" +) + +type apiTests struct { + path string + body []byte + + echoMsg string + echoErr string +} + +func init() { + err := register("/api-tests:", + &appInfo{ + appType: reflect.TypeOf(apiTests{}), + isNative: true, + tablesToWatch: nil}) + + if err != nil { + glog.Fatalf("Failed to register ApiTest app; %v", err) + } +} + +func (app *apiTests) initialize(inp appData) { + app.path = inp.path + app.body = inp.payload +} + +func (app *apiTests) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + return nil, app.translatePath() +} + +func (app *apiTests) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + return nil, app.translatePath() +} + +func (app *apiTests) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + return nil, app.translatePath() +} + +func (app *apiTests) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + return nil, app.translatePath() +} + +func (app *apiTests) translateGet(dbs [db.MaxDB]*db.DB) error { + return app.translatePath() +} + +func (app *apiTests) translateAction(dbs [db.MaxDB]*db.DB) error { + var req struct { + Input struct { + Message string `json:"message"` + ErrType string `json:"error-type"` + } `json:"api-tests:input"` + } + + err := json.Unmarshal(app.body, &req) + if err != nil { + glog.Errorf("Failed to parse rpc input; err=%v", err) + return tlerr.InvalidArgs("Invalid rpc input") + } + + app.echoMsg = req.Input.Message + app.echoErr = req.Input.ErrType + + return nil +} + +func (app *apiTests) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + return nil, nil, nil +} + +func (app *apiTests) processCreate(d *db.DB) (SetResponse, error) { + return app.processSet() +} + +func (app *apiTests) processUpdate(d *db.DB) (SetResponse, error) { + return app.processSet() +} + +func (app *apiTests) processReplace(d *db.DB) (SetResponse, error) { + return app.processSet() +} + +func (app *apiTests) processDelete(d *db.DB) (SetResponse, error) { + return app.processSet() +} + +func (app *apiTests) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + var gr GetResponse + err := app.getError() + if err == nil { + gr.Payload, err = json.Marshal(&app.echoMsg) + } + return gr, err +} + +func (app *apiTests) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) { + var ar ActionResponse + + err := app.getError() + if err == nil { + var respData struct { + Output struct { + Message string `json:"message"` + } `json:"api-tests:output"` + } + + respData.Output.Message = app.echoMsg + ar.Payload, err = json.Marshal(&respData) + } + + return ar, err +} + +func (app *apiTests) translatePath() error { + app.echoMsg = "Hello, world!" + k := strings.Index(app.path, "error/") + if k >= 0 { + app.echoErr = app.path[k+6:] + } + return nil +} + +func (app *apiTests) processSet() (SetResponse, error) { + var sr SetResponse + err := app.getError() + return sr, err +} + +func (app *apiTests) getError() error { + switch strings.ToLower(app.echoErr) { + case "invalid-args", "invalidargs": + return tlerr.InvalidArgs(app.echoMsg) + case "exists": + return tlerr.AlreadyExists(app.echoMsg) + case "not-found", "notfound": + return tlerr.NotFound(app.echoMsg) + case "not-supported", "notsupported", "unsupported": + return tlerr.NotSupported(app.echoMsg) + case "", "no", "none", "false": + return nil + default: + return tlerr.New(app.echoMsg) + } +} diff --git a/src/translib/app_interface.go b/src/translib/app_interface.go new file mode 100644 index 0000000000..3e008822f0 --- /dev/null +++ b/src/translib/app_interface.go @@ -0,0 +1,169 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 + +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 + +import ( + "errors" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" + "reflect" + "strings" + "translib/db" +) + +//Structure containing app module information +type appInfo struct { + appType reflect.Type + ygotRootType reflect.Type + isNative bool + tablesToWatch []*db.TableSpec +} + +//Structure containing the app data coming from translib infra +type appData struct { + path string + payload []byte + ygotRoot *ygot.GoStruct + ygotTarget *interface{} +} + +//map containing the base path to app module info +var appMap map[string]*appInfo + +//array containing all the supported models +var models []ModelData + +//Interface for all App Modules +type appInterface interface { + initialize(data appData) + translateCreate(d *db.DB) ([]db.WatchKeys, error) + translateUpdate(d *db.DB) ([]db.WatchKeys, error) + translateReplace(d *db.DB) ([]db.WatchKeys, error) + translateDelete(d *db.DB) ([]db.WatchKeys, error) + translateGet(dbs [db.MaxDB]*db.DB) error + translateAction(dbs [db.MaxDB]*db.DB) error + translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) + processCreate(d *db.DB) (SetResponse, error) + processUpdate(d *db.DB) (SetResponse, error) + processReplace(d *db.DB) (SetResponse, error) + processDelete(d *db.DB) (SetResponse, error) + processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) + processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) +} + +//App modules will use this function to register with App interface during boot up +func register(path string, info *appInfo) error { + var err error + log.Info("Registering for path =", path) + + if appMap == nil { + appMap = make(map[string]*appInfo) + } + + if _, ok := appMap[path]; ok == false { + + appMap[path] = info + + } else { + log.Fatal("Duplicate path being registered. Path =", path) + err = errors.New("Duplicate path") + } + + return err +} + +//Adds the model information to the supported models array +func addModel(model *ModelData) error { + var err error + + models = append(models, *model) + + //log.Info("Models = ", models) + return err +} + +//App modules can use this function to unregister itself from the app interface +func unregister(path string) error { + var err error + log.Info("Unregister for path =", path) + + _, ok := appMap[path] + + if ok { + log.Info("deleting for path =", path) + delete(appMap, path) + } + + return err +} + +//Translib infra will use this function get the app info for a given path +func getAppModuleInfo(path string) (*appInfo, error) { + var err error + log.Info("getAppModule called for path =", path) + + for pattern, app := range appMap { + if !strings.HasPrefix(path, pattern) { + continue + } + + log.Info("found the entry in the map for path =", pattern) + + return app, err + } + + /* 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 +} + +//Get all the supported models +func getModels() []ModelData { + + return models +} + +//Creates a new app from the appType and returns it as an appInterface +func getAppInterface(appType reflect.Type) (appInterface, error) { + var err error + appInstance := reflect.New(appType) + app, ok := appInstance.Interface().(appInterface) + + if !ok { + err = errors.New("Invalid appType") + log.Fatal("Appmodule does not confirm to appInterface method conventions for appType=", appType) + } else { + log.Info("cast to appInterface worked", app) + } + + return app, err +} diff --git a/src/translib/app_utils.go b/src/translib/app_utils.go new file mode 100644 index 0000000000..2be233953b --- /dev/null +++ b/src/translib/app_utils.go @@ -0,0 +1,239 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "bytes" + "encoding/json" + "reflect" + "strings" + "translib/db" + "translib/ocbinds" + "translib/tlerr" + + log "github.com/golang/glog" + "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" + "github.com/openconfig/ygot/ytypes" +) + +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 getYangPathFromYgotStruct(s ygot.GoStruct, yangPathPrefix string, appModuleName string) string { + tn := reflect.TypeOf(s).Elem().Name() + schema, ok := ocbinds.SchemaTree[tn] + if !ok { + log.Errorf("could not find schema for type %s", tn) + return "" + } else if schema != nil { + yPath := schema.Path() + //yPath = strings.Replace(yPath, "/device/acl", "/openconfig-acl:acl", 1) + yPath = strings.Replace(yPath, yangPathPrefix, appModuleName, 1) + return yPath + } + return "" +} + +func generateGetResponsePayload(targetUri string, deviceObj *ocbinds.Device, ygotTarget *interface{}) ([]byte, error) { + var err error + var payload []byte + + if len(targetUri) == 0 { + return payload, tlerr.InvalidArgs("GetResponse failed as target Uri is not valid") + } + path, err := ygot.StringToPath(targetUri, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + return payload, tlerr.InvalidArgs("URI to path conversion failed: %v", err) + } + + // Get current node (corresponds to ygotTarget) and its parent node + var pathList []*gnmi.PathElem = path.Elem + parentPath := &gnmi.Path{} + for i := 0; i < len(pathList); i++ { + if log.V(3) { + log.Infof("pathList[%d]: %s\n", i, pathList[i]) + } + pathSlice := strings.Split(pathList[i].Name, ":") + pathList[i].Name = pathSlice[len(pathSlice)-1] + if i < (len(pathList) - 1) { + parentPath.Elem = append(parentPath.Elem, pathList[i]) + } + } + parentNodeList, err := ytypes.GetNode(ygSchema.RootSchema(), deviceObj, parentPath) + if err != nil { + return payload, err + } + if len(parentNodeList) == 0 { + return payload, tlerr.InvalidArgs("Invalid URI: %s", targetUri) + } + parentNode := parentNodeList[0].Data + + currentNodeList, err := ytypes.GetNode(ygSchema.RootSchema(), deviceObj, path, &(ytypes.GetPartialKeyMatch{})) + if err != nil { + return payload, err + } + if len(currentNodeList) == 0 { + return payload, tlerr.NotFound("Resource not found") + } + //currentNode := currentNodeList[0].Data + currentNodeYangName := currentNodeList[0].Schema.Name + + // Create empty clone of parent node + parentNodeClone := reflect.New(reflect.TypeOf(parentNode).Elem()) + var parentCloneObj ygot.ValidatedGoStruct + var ok bool + if parentCloneObj, ok = (parentNodeClone.Interface()).(ygot.ValidatedGoStruct); ok { + ygot.BuildEmptyTree(parentCloneObj) + pcType := reflect.TypeOf(parentCloneObj).Elem() + pcValue := reflect.ValueOf(parentCloneObj).Elem() + + var currentNodeOCFieldName string + for i := 0; i < pcValue.NumField(); i++ { + fld := pcValue.Field(i) + fldType := pcType.Field(i) + if fldType.Tag.Get("path") == currentNodeYangName { + currentNodeOCFieldName = fldType.Name + // Take value from original parent and set in parent clone + valueFromParent := reflect.ValueOf(parentNode).Elem().FieldByName(currentNodeOCFieldName) + fld.Set(valueFromParent) + break + } + } + if log.V(3) { + log.Infof("Target yang name: %s OC Field name: %s\n", currentNodeYangName, currentNodeOCFieldName) + } + } + + payload, err = dumpIetfJson(parentCloneObj, true) + + return payload, err +} + +func getTargetNodeYangSchema(targetUri string, deviceObj *ocbinds.Device) (*yang.Entry, error) { + if len(targetUri) == 0 { + return nil, tlerr.InvalidArgs("GetResponse failed as target Uri is not valid") + } + path, err := ygot.StringToPath(targetUri, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + return nil, tlerr.InvalidArgs("URI to path conversion failed: %v", err) + } + // Get current node (corresponds to ygotTarget) + var pathList []*gnmi.PathElem = path.Elem + for i := 0; i < len(pathList); i++ { + if log.V(3) { + log.Infof("pathList[%d]: %s\n", i, pathList[i]) + } + pathSlice := strings.Split(pathList[i].Name, ":") + pathList[i].Name = pathSlice[len(pathSlice)-1] + } + targetNodeList, err := ytypes.GetNode(ygSchema.RootSchema(), deviceObj, path, &(ytypes.GetPartialKeyMatch{})) + if err != nil { + return nil, tlerr.InvalidArgs("Getting node information failed: %v", err) + } + if len(targetNodeList) == 0 { + return nil, tlerr.NotFound("Resource not found") + } + targetNodeSchema := targetNodeList[0].Schema + //targetNode := targetNodeList[0].Data + if log.V(3) { + log.Infof("Target node yang name: %s\n", targetNodeSchema.Name) + } + return targetNodeSchema, nil +} + +func dumpIetfJson(s ygot.ValidatedGoStruct, skipValidation bool) ([]byte, error) { + jsonStr, err := ygot.EmitJSON(s, &ygot.EmitJSONConfig{ + Format: ygot.RFC7951, + Indent: " ", + SkipValidation: skipValidation, + RFC7951Config: &ygot.RFC7951JSONConfig{ + AppendModuleName: true, + }, + }) + var buf bytes.Buffer + json.Compact(&buf, []byte(jsonStr)) + return []byte(buf.String()), err +} + +func contains(sl []string, str string) bool { + for _, v := range sl { + if v == str { + return true + } + } + return false +} + +func removeElement(sl []string, str string) []string { + for i := 0; i < len(sl); i++ { + if sl[i] == str { + sl = append(sl[:i], sl[i+1:]...) + i-- + sl = sl[:len(sl)] + break + } + } + return sl +} + +// isNotFoundError return true if the error is a 'not found' error +func isNotFoundError(err error) bool { + switch err.(type) { + case tlerr.TranslibRedisClientEntryNotExist, tlerr.NotFoundError: + return true + default: + return false + } +} + +// asKey cretaes a db.Key from given key components +func asKey(parts ...string) db.Key { + return db.Key{Comp: parts} +} + +func createEmptyDbValue(fieldName string) db.Value { + return db.Value{Field: map[string]string{fieldName: ""}} +} + +/* Check if targetUriPath is child (subtree) of nodePath +The return value can be used to decide if subtrees needs +to visited to fill the data or not. +*/ +func isSubtreeRequest(targetUriPath string, nodePath string) bool { + return strings.HasPrefix(targetUriPath, nodePath) +} diff --git a/src/translib/authorize.go b/src/translib/authorize.go new file mode 100644 index 0000000000..e235c1f564 --- /dev/null +++ b/src/translib/authorize.go @@ -0,0 +1,61 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 to authorize + +an incoming user. It also includes caching of the UserDB data + +needed to authorize the user. + +*/ + +package translib + +import ( + //"strconv" + //log "github.com/golang/glog" +) + +//TODO:define maps for storing the UserDB cache + +func init() { + //TODO:Allocate the maps and populate them here +} + +//TODO:Subscribe to UserDB changes and repopulate the cache on any notification + +func isUserAuthorizedForSet(user string) bool { + //TODO:Need code to get role from username and then authorize based on that + //This is a temporary code, this needs to be fixed + + if ((user == "") || (user == "admin")) { + return true + } + + return false +} + +func isUserAuthorizedForGet(user string) bool { + return true +} + +func isUserAuthorizedForAction(user string) bool { + return true +} diff --git a/src/translib/cmn_utils_test.go b/src/translib/cmn_utils_test.go new file mode 100644 index 0000000000..b8ca266969 --- /dev/null +++ b/src/translib/cmn_utils_test.go @@ -0,0 +1,107 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "os" + "testing" + db "translib/db" +) + +func TestMain(m *testing.M) { + if err := clearAclDataFromDb(); err != nil { + os.Exit(-1) + } + fmt.Println("+++++ Removed All Acl Data from Db +++++") + + if err := clearLagDataFromDb(); err != nil { + os.Exit(-1) + } + fmt.Println("+++++ Removed All PortChannel Data from Db before tests +++++") + + ret := m.Run() + + if err := clearAclDataFromDb(); err != nil { + os.Exit(-1) + } + + if err := clearLagDataFromDb(); err != nil { + os.Exit(-1) + } + fmt.Println("+++++ Removed All PortChannel Data from Db after the tests +++++") + + os.Exit(ret) +} + +func processGetRequest(url string, expectedRespJson string, errorCase bool) func(*testing.T) { + return func(t *testing.T) { + response, err := Get(GetRequest{Path:url}) + if err != nil && !errorCase { + t.Errorf("Error %v received for Url: %s", err, url) + } + + respJson := response.Payload + if string(respJson) != expectedRespJson { + t.Errorf("Response for Url: %s received is not expected:\n%s", url, string(respJson)) + } + } +} + +func processSetRequest(url string, jsonPayload string, oper string, errorCase bool) func(*testing.T) { + return func(t *testing.T) { + var err error + switch oper { + case "POST": + _, err = Create(SetRequest{Path: url, Payload: []byte(jsonPayload)}) + case "PATCH": + _, err = Update(SetRequest{Path: url, Payload: []byte(jsonPayload)}) + case "PUT": + _, err = Replace(SetRequest{Path: url, Payload: []byte(jsonPayload)}) + default: + t.Errorf("Operation not supported") + } + if err != nil && !errorCase { + t.Errorf("Error %v received for Url: %s", err, url) + } + } +} + +func processDeleteRequest(url string) func(*testing.T) { + return func(t *testing.T) { + _, err := Delete(SetRequest{Path: url}) + if err != nil { + t.Errorf("Error %v received for Url: %s", err, url) + } + } +} + +func getConfigDb() *db.DB { + configDb, _ := db.NewDB(db.Options{ + DBNo: db.ConfigDB, + InitIndicator: "CONFIG_DB_INITIALIZED", + TableNameSeparator: "|", + KeySeparator: "|", + }) + + return configDb +} + +var emptyJson string = "{}" diff --git a/src/translib/common_app.go b/src/translib/common_app.go new file mode 100644 index 0000000000..0ec5d56e98 --- /dev/null +++ b/src/translib/common_app.go @@ -0,0 +1,572 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "cvl" +) + +var () + +type CommonApp struct { + pathInfo *PathInfo + body []byte + 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, body: data.payload, 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) translateAction(dbs [db.MaxDB]*db.DB) error { + var err error + log.Info("translateAction:path =", app.pathInfo.Path, app.body) + return 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) + var txCache interface{} + + for { + payload, err = transformer.GetAndXlateFromDB(app.pathInfo.Path, app.ygotRoot, dbs, txCache) + if err != nil { + log.Error("transformer.transformer.GetAndXlateFromDB failure. error:", err) + resPayload = payload + break + } + + targetObj, tgtObjCastOk := (*app.ygotTarget).(ygot.GoStruct) + if tgtObjCastOk == false { + /*For ygotTarget populated by tranlib, for query on leaf level and list(without instance) level, + casting to GoStruct fails so use the parent node of ygotTarget to Unmarshall the payload into*/ + log.Infof("Use GetParentNode() since casting ygotTarget to GoStruct failed(uri - %v", app.pathInfo.Path) + targetUri := app.pathInfo.Path + parentTargetObj, _, getParentNodeErr := getParentNode(&targetUri, (*app.ygotRoot).(*ocbinds.Device)) + if getParentNodeErr != nil { + log.Warningf("getParentNode() failure for uri %v", app.pathInfo.Path) + resPayload = payload + break + } + if parentTargetObj != nil { + targetObj, tgtObjCastOk = (*parentTargetObj).(ygot.GoStruct) + if tgtObjCastOk == false { + log.Warningf("Casting of parent object returned from getParentNode() to GoStruct failed(uri - %v)", app.pathInfo.Path) + resPayload = payload + break + } + } else { + log.Warningf("getParentNode() returned a nil Object for uri %v", app.pathInfo.Path) + resPayload = payload + break + } + } + if targetObj != nil { + err = ocbinds.Unmarshal(payload, targetObj) + if err != nil { + log.Error("ocbinds.Unmarshal() failed. error:", err) + resPayload = payload + break + } + + resPayload, err = generateGetResponsePayload(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device), app.ygotTarget) + if err != nil { + log.Error("generateGetResponsePayload() failed") + resPayload = payload + } + break + } else { + log.Warning("processGet. targetObj is null. Unable to Unmarshal payload") + resPayload = payload + break + } + } + + return GetResponse{Payload: resPayload}, err +} + +func (app *CommonApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) { + var resp ActionResponse + err := errors.New("Not implemented") + + resp.Payload, err = transformer.CallRpcMethod(app.pathInfo.Path, app.body, dbs) + log.Info("transformer.XlateToDb() returned", resp.Payload) + + return resp, 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 txCache interface{} + log.Info("translateCRUDCommon:path =", app.pathInfo.Path) + + // translate yang to db + result, err := transformer.XlateToDb(app.pathInfo.Path, opcode, d, (*app).ygotRoot, (*app).ygotTarget, txCache) + 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 + + moduleNm, err := transformer.GetModuleNmFromPath(app.pathInfo.Path) + if (err != nil) || (len(moduleNm) == 0) { + log.Error("GetModuleNmFromPath() failed") + return keys, err + } + + var resultTblList []string + for tblnm, _ := range result { //Get dependency list for all tables in result + resultTblList = append(resultTblList, tblnm) + } + log.Info("Result Tables List", resultTblList) + + // Get list of tables to watch + depTbls := transformer.GetTablesToWatch(resultTblList, moduleNm) + if len(depTbls) == 0 { + log.Errorf("Failure to get Tables to watch for module %v", moduleNm) + err = errors.New("GetTablesToWatch returned empty slice") + return keys, err + } + for _, tbl := range depTbls { + tblsToWatch = append(tblsToWatch, &db.TableSpec{Name: tbl}) + } + log.Info("Tables to watch", tblsToWatch) + cmnAppInfo.tablesToWatch = tblsToWatch + + // Get sorted dependency tables to be used for CRUD operations + cvSess, cvlRetSess := cvl.ValidationSessOpen() + if cvlRetSess != cvl.CVL_SUCCESS { + log.Errorf("Failure in creating CVL validation session object required to use CVl API to get Tbl info for module %v - %v", moduleNm, cvlRetSess) + return keys, err + } + cvlSortDepTblList, cvlRetDepTbl := cvSess.SortDepTables(resultTblList) + if cvlRetDepTbl != cvl.CVL_SUCCESS { + log.Warningf("Failure in cvlSess.SortDepTables: %v", cvlRetDepTbl) + cvl.ValidationSessClose(cvSess) + return keys, err + } + log.Info("cvlSortDepTblList = ", cvlSortDepTblList) + app.cmnAppOrdTbllist = cvlSortDepTblList + + cvl.ValidationSessClose(cvSess) + + 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 + + /* CVL sorted order is in child first, parent later order. CRU ops from parent first order */ + for idx := len(app.cmnAppOrdTbllist)-1; idx >= 0; idx-- { + tblNm := app.cmnAppOrdTbllist[idx] + 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 + var moduleNm string + + /* Retrieve module Name */ + moduleNm, err = transformer.GetModuleNmFromPath(app.pathInfo.Path) + if (err != nil) || (len(moduleNm) == 0) { + log.Error("GetModuleNmFromPath() failed") + return err + } + log.Info("getModuleNmFromPath() returned module name = ", moduleNm) + + /* app.cmnAppOrdTbllist has child first, parent later order */ + 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") + ordTblList := transformer.GetOrdTblList(tblNm, moduleNm) + if len(ordTblList) == 0 { + log.Error("GetOrdTblList returned empty slice") + err = errors.New("GetOrdTblList returned empty slice. Insufficient information to process request") + return err + } + log.Infof("GetOrdTblList for table - %v, module %v returns %v", tblNm, moduleNm, ordTblList) + if len(tblVal) == 0 { + log.Info("DELETE case - No table instances/rows found hence delete entire table = ", tblNm) + for _, ordtbl := range ordTblList { + log.Info("Since parent table is to be deleted, first deleting child table = ", ordtbl) + if ordtbl == tblNm { + // Handle the child tables only till you reach the parent table entry + break + } + dbTblSpec = &db.TableSpec{Name: ordtbl} + err = d.DeleteTable(dbTblSpec) + if err != nil { + log.Warning("DELETE case - d.DeleteTable() failure for Table = ", ordtbl) + 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) + // Continue to repeat ordered deletion for all tables + continue + + } + + 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 _, ordtbl := range ordTblList { + if ordtbl == tblNm { + // Handle the child tables only till you reach the parent table entry + break; + } + dbTblSpec = &db.TableSpec{Name: ordtbl} + 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 = ", ordtbl) + return err + } + log.Info("Deleted keys matching parent table key pattern for child table = ", ordtbl) + + } + 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) + valueLst := strings.Split(value, ",") + if len(exstLst) != 0 { + 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) + } + } else if opcode == UPDATE { + exstLst = valueLst + } + 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 new file mode 100644 index 0000000000..5f4e53357b --- /dev/null +++ b/src/translib/db/db.go @@ -0,0 +1,1430 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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. + +There may be an attempt to mimic sonic-py-swsssdk to ease porting of +code written in python using that SDK to Go Language. + +Example: + + * Initialization: + + d, _ := db.NewDB(db.Options { + DBNo : db.ConfigDB, + InitIndicator : "CONFIG_DB_INITIALIZED", + TableNameSeparator: "|", + KeySeparator : "|", + }) + + * Close: + + d.DeleteDB() + + + * No-Transaction SetEntry + + tsa := db.TableSpec { Name: "ACL_TABLE" } + tsr := db.TableSpec { Name: "ACL_RULE" } + + ca := make([]string, 1, 1) + + ca[0] = "MyACL1_ACL_IPV4" + akey := db.Key { Comp: ca} + avalue := db.Value {map[string]string {"ports":"eth0","type":"mirror" }} + + d.SetEntry(&tsa, akey, avalue) + + * GetEntry + + avalue, _ := d.GetEntry(&tsa, akey) + + * GetKeys + + keys, _ := d.GetKeys(&tsa); + + * No-Transaction DeleteEntry + + d.DeleteEntry(&tsa, akey) + + * GetTable + + ta, _ := d.GetTable(&tsa) + + * No-Transaction DeleteTable + + d.DeleteTable(&ts) + + * Transaction + + rkey := db.Key { Comp: []string { "MyACL2_ACL_IPV4", "RULE_1" }} + rvalue := db.Value { Field: map[string]string { + "priority" : "0", + "packet_action" : "eth1", + }, + } + + d.StartTx([]db.WatchKeys { {Ts: &tsr, Key: &rkey} }, + []*db.TableSpec { &tsa, &tsr }) + + d.SetEntry( &tsa, akey, avalue) + d.SetEntry( &tsr, rkey, rvalue) + + e := d.CommitTx() + + * Transaction Abort + + d.StartTx([]db.WatchKeys {}, + []*db.TableSpec { &tsa, &tsr }) + d.DeleteEntry( &tsa, rkey) + d.AbortTx() + + +*/ +package db + +import ( + "fmt" + "strconv" + + // "reflect" + "errors" + "strings" + + "github.com/go-redis/redis" + "github.com/golang/glog" + "cvl" + "translib/tlerr" +) + +const ( + DefaultRedisUNIXSocket string = "/var/run/redis/redis.sock" + DefaultRedisLocalTCPEP string = "localhost:6379" + DefaultRedisRemoteTCPEP string = "127.0.0.1:6379" +) + +func init() { +} + +// DBNum type indicates the type of DB (Eg: ConfigDB, ApplDB, ...). +type DBNum int + +const ( + ApplDB DBNum = iota // 0 + AsicDB // 1 + CountersDB // 2 + LogLevelDB // 3 + ConfigDB // 4 + FlexCounterDB // 5 + StateDB // 6 + SnmpDB // 7 + ErrorDB // 8 + // All DBs added above this line, please ---- + MaxDB // The Number of DBs +) + +func(dbNo DBNum) String() string { + return fmt.Sprintf("%d", dbNo) +} + +// Options gives parameters for opening the redis client. +type Options struct { + DBNo DBNum + InitIndicator string + TableNameSeparator string + KeySeparator string + IsWriteDisabled bool //Indicated if write is allowed + + DisableCVLCheck bool +} + +func (o Options) String() string { + return fmt.Sprintf( + "{ DBNo: %v, InitIndicator: %v, TableNameSeparator: %v, KeySeparator: %v , DisableCVLCheck: %v }", + o.DBNo, o.InitIndicator, o.TableNameSeparator, o.KeySeparator, + o.DisableCVLCheck) +} + +type _txState int + +const ( + txStateNone _txState = iota // Idle (No transaction) + txStateWatch // WATCH issued + txStateSet // At least one Set|Mod|Delete done. + txStateMultiExec // Between MULTI & EXEC +) + +func (s _txState) String() string { + var state string + switch s { + case txStateNone: + state = "txStateNone" + case txStateWatch: + state = "txStateWatch" + case txStateSet: + state = "txStateSet" + case txStateMultiExec: + state = "txStateMultiExec" + default: + state = "Unknown _txState" + } + return state +} + +const ( + InitialTxPipelineSize int = 100 +) + +// TableSpec gives the name of the table, and other per-table customizations. +// (Eg: { Name: ACL_TABLE" }). +type TableSpec struct { + Name string + // https://github.com/project-arlo/sonic-mgmt-framework/issues/29 + // CompCt tells how many components in the key. Only the last component + // can have TableSeparator as part of the key. Otherwise, we cannot + // tell where the key component begins. + CompCt int +} + +// Key gives the key components. +// (Eg: { Comp : [] string { "acl1", "rule1" } } ). +type Key struct { + Comp []string +} + +func (k Key) String() string { + return fmt.Sprintf("{ Comp: %v }", k.Comp) +} + +func (v Value) String () string { + var str string + for k, v1 := range v.Field { + str = str + fmt.Sprintf("\"%s\": \"%s\"\n", k, v1) + } + + return str +} + +// Value gives the fields as a map. +// (Eg: { Field: map[string]string { "type" : "l3v6", "ports" : "eth0" } } ). +type Value struct { + Field map[string]string +} + +// Table gives the entire table a a map. +// (Eg: { ts: &TableSpec{ Name: "ACL_TABLE" }, +// entry: map[string]Value { +// "ACL_TABLE|acl1|rule1_1": Value { +// Field: map[string]string { +// "type" : "l3v6", "ports" : "Ethernet0", +// } +// }, +// "ACL_TABLE|acl1|rule1_2": Value { +// Field: map[string]string { +// "type" : "l3v6", "ports" : "eth0", +// } +// }, +// } +// }) + +type Table struct { + ts *TableSpec + entry map[string]Value + db *DB +} + +type _txOp int + +const ( + txOpNone _txOp = iota // No Op + txOpHMSet // key, value gives the field:value to be set in key + txOpHDel // key, value gives the fields to be deleted in key + txOpDel // key +) + +type _txCmd struct { + ts *TableSpec + op _txOp + key *Key + value *Value +} + +// DB is the main type. +type DB struct { + client *redis.Client + Opts *Options + + txState _txState + txCmds []_txCmd + cv *cvl.CVL + cvlEditConfigData [] cvl.CVLEditConfigData + +/* + sKeys []*SKey // Subscribe Key array + sHandler HFunc // Handler Function + sCh <-chan *redis.Message // non-Nil implies SubscribeDB +*/ + sPubSub *redis.PubSub // PubSub. non-Nil implies SubscribeDB + sCIP bool // Close in Progress +} + +func (d DB) String() string { + return fmt.Sprintf("{ client: %v, Opts: %v, txState: %v, tsCmds: %v }", + d.client, d.Opts, d.txState, d.txCmds) +} + +// NewDB is the factory method to create new DB's. +func NewDB(opt Options) (*DB, error) { + + var e error + + if glog.V(3) { + glog.Info("NewDB: Begin: opt: ", opt) + } + + d := DB{client: redis.NewClient(&redis.Options{ + Network: "tcp", + Addr: DefaultRedisLocalTCPEP, + //Addr: DefaultRedisRemoteTCPEP, + Password: "", /* TBD */ + // DB: int(4), /* CONFIG_DB DB No. */ + DB: int(opt.DBNo), + DialTimeout: 0, + // For Transactions, limit the pool + PoolSize: 1, + // Each DB gets it own (single) connection. + }), + Opts: &opt, + txState: txStateNone, + txCmds: make([]_txCmd, 0, InitialTxPipelineSize), + cvlEditConfigData: make([]cvl.CVLEditConfigData, 0, InitialTxPipelineSize), + } + + if d.client == nil { + glog.Error("NewDB: Could not create redis client") + e = tlerr.TranslibDBCannotOpen { } + goto NewDBExit + } + + if opt.DBNo != ConfigDB { + if glog.V(3) { + glog.Info("NewDB: ! ConfigDB. Skip init. check.") + } + goto NewDBSkipInitIndicatorCheck + } + + if len(d.Opts.InitIndicator) == 0 { + + glog.Info("NewDB: Init indication not requested") + + } else if init, _ := d.client.Get(d.Opts.InitIndicator).Int(); init != 1 { + + glog.Error("NewDB: Database not inited") + e = tlerr.TranslibDBNotInit { } + goto NewDBExit + } + +NewDBSkipInitIndicatorCheck: + +NewDBExit: + + if glog.V(3) { + glog.Info("NewDB: End: d: ", d, " e: ", e) + } + + return &d, e +} + +// DeleteDB is the gentle way to close the DB connection. +func (d *DB) DeleteDB() error { + + if glog.V(3) { + glog.Info("DeleteDB: Begin: d: ", d) + } + + if d.txState != txStateNone { + glog.Warning("DeleteDB: not txStateNone, txState: ", d.txState) + } + + return d.client.Close() +} + +func (d *DB) key2redis(ts *TableSpec, key Key) string { + + if glog.V(5) { + glog.Info("key2redis: Begin: ", + ts.Name+ + d.Opts.TableNameSeparator+ + strings.Join(key.Comp, d.Opts.KeySeparator)) + } + return ts.Name + + d.Opts.TableNameSeparator + + strings.Join(key.Comp, d.Opts.KeySeparator) +} + +func (d *DB) redis2key(ts *TableSpec, redisKey string) Key { + + splitTable := strings.SplitN(redisKey, d.Opts.TableNameSeparator, 2) + + if ts.CompCt > 0 { + return Key{strings.SplitN(splitTable[1],d.Opts.KeySeparator, ts.CompCt)} + } else { + return Key{strings.Split(splitTable[1], d.Opts.KeySeparator)} + } + +} + +func (d *DB) ts2redisUpdated(ts *TableSpec) string { + + if glog.V(5) { + glog.Info("ts2redisUpdated: Begin: ", ts.Name) + } + + var updated string + + if strings.Contains(ts.Name, "*") { + updated = string("CONFIG_DB_UPDATED") + } else { + updated = string("CONFIG_DB_UPDATED_") + ts.Name + } + + return updated +} + +// GetEntry retrieves an entry(row) from the table. +func (d *DB) GetEntry(ts *TableSpec, key Key) (Value, error) { + + if glog.V(3) { + glog.Info("GetEntry: Begin: ", "ts: ", ts, " key: ", key) + } + + var value Value + + /* + m := make(map[string]string) + m["f0.0"] = "v0.0" + m["f0.1"] = "v0.1" + m["f0.2"] = "v0.2" + v := Value{Field: m} + */ + + v, e := d.client.HGetAll(d.key2redis(ts, key)).Result() + + if len(v) != 0 { + value = Value{Field: v} + } else { + if glog.V(4) { + glog.Info("GetEntry: HGetAll(): empty map") + } + // e = errors.New("Entry does not exist") + e = tlerr.TranslibRedisClientEntryNotExist { Entry: d.key2redis(ts, key) } + } + + if glog.V(3) { + glog.Info("GetEntry: End: ", "value: ", value, " e: ", e) + } + + return value, e +} + +// GetKeys retrieves all entry/row keys. +func (d *DB) GetKeys(ts *TableSpec) ([]Key, error) { + + if glog.V(3) { + glog.Info("GetKeys: Begin: ", "ts: ", ts) + } + + /* + k := []Key{ + {[]string{"k0.0", "k0.1"}}, + {[]string{"k1.0", "k1.1"}}, + } + */ + redisKeys, e := d.client.Keys(d.key2redis(ts, + Key{Comp: []string{"*"}})).Result() + if glog.V(4) { + glog.Info("GetKeys: redisKeys: ", redisKeys, " e: ", e) + } + + keys := make([]Key, 0, len(redisKeys)) + for i := 0; i < len(redisKeys); i++ { + keys = append(keys, d.redis2key(ts, redisKeys[i])) + } + + if glog.V(3) { + glog.Info("GetKeys: End: ", "keys: ", keys, " e: ", e) + } + + return keys, e +} + +// DeleteKeys deletes all entry/row keys matching a pattern. +func (d *DB) DeleteKeys(ts *TableSpec, key Key) error { + if glog.V(3) { + glog.Info("DeleteKeys: Begin: ", "ts: ", ts, " key: ", key) + } + + // This can be done via a LUA script as well. For now do this. TBD + redisKeys, e := d.client.Keys(d.key2redis(ts, key)).Result() + if glog.V(4) { + glog.Info("DeleteKeys: redisKeys: ", redisKeys, " e: ", e) + } + + for i := 0; i < len(redisKeys); i++ { + if glog.V(4) { + glog.Info("DeleteKeys: Deleting redisKey: ", redisKeys[i]) + } + e = d.DeleteEntry(ts, d.redis2key(ts, redisKeys[i])) + if e != nil { + glog.Warning("DeleteKeys: Deleting: ts: ", ts, " key", + d.redis2key(ts, redisKeys[i]), " : ", e) + } + } + + if glog.V(3) { + glog.Info("DeleteKeys: End: e: ", e) + } + return e +} + + +func (d *DB) doCVL(ts * TableSpec, cvlOps []cvl.CVLOperation, key Key, vals []Value) error { + var e error = nil + + var cvlRetCode cvl.CVLRetCode + var cei cvl.CVLErrorInfo + + if d.Opts.DisableCVLCheck { + glog.Info("doCVL: CVL Disabled. Skipping CVL") + goto doCVLExit + } + + // No Transaction case. No CVL. + if d.txState == txStateNone { + glog.Info("doCVL: No Transactions. Skipping CVL") + goto doCVLExit + } + + if len(cvlOps) != len(vals) { + glog.Error("doCVL: Incorrect arguments len(cvlOps) != len(vals)") + e = errors.New("CVL Incorrect args") + return e + } + for i := 0; i < len(cvlOps); i++ { + + cvlEditConfigData := cvl.CVLEditConfigData { + VType: cvl.VALIDATE_ALL, + VOp: cvlOps[i], + Key: d.key2redis(ts, key), + } + + switch cvlOps[i] { + case cvl.OP_CREATE, cvl.OP_UPDATE: + cvlEditConfigData.Data = vals[i].Field + d.cvlEditConfigData = append(d.cvlEditConfigData, cvlEditConfigData) + + case cvl.OP_DELETE: + if len(vals[i].Field) == 0 { + cvlEditConfigData.Data = map[string]string {} + } else { + cvlEditConfigData.Data = vals[i].Field + } + d.cvlEditConfigData = append(d.cvlEditConfigData, cvlEditConfigData) + + default: + glog.Error("doCVL: Unknown, op: ", cvlOps[i]) + e = errors.New("Unknown Op: " + string(cvlOps[i])) + } + + } + + if e != nil { + goto doCVLExit + } + + if glog.V(3) { + glog.Info("doCVL: calling ValidateEditConfig: ", d.cvlEditConfigData) + } + + cei, cvlRetCode = d.cv.ValidateEditConfig(d.cvlEditConfigData) + + if cvl.CVL_SUCCESS != cvlRetCode { + glog.Error("doCVL: CVL Failure: " , cvlRetCode) + // e = errors.New("CVL Failure: " + string(cvlRetCode)) + e = tlerr.TranslibCVLFailure { Code: int(cvlRetCode), + CVLErrorInfo: cei } + glog.Error("doCVL: " , len(d.cvlEditConfigData), len(cvlOps)) + d.cvlEditConfigData = d.cvlEditConfigData[:len(d.cvlEditConfigData) - len(cvlOps)] + } else { + for i := 0; i < len(cvlOps); i++ { + d.cvlEditConfigData[len(d.cvlEditConfigData)-1-i].VType = cvl.VALIDATE_NONE; + } + } + +doCVLExit: + + if glog.V(3) { + glog.Info("doCVL: End: e: ", e) + } + + return e +} + +func (d *DB) doWrite(ts * TableSpec, op _txOp, key Key, val interface{}) error { + var e error = nil + var value Value + + if d.Opts.IsWriteDisabled { + glog.Error("doWrite: Write to DB disabled") + e = errors.New("Write to DB disabled during this operation") + goto doWriteExit + } + + switch d.txState { + case txStateNone: + if glog.V(2) { + glog.Info("doWrite: No Transaction.") + } + break + case txStateWatch: + if glog.V(2) { + glog.Info("doWrite: Change to txStateSet, txState: ", d.txState) + } + d.txState = txStateSet + break + case txStateSet: + if glog.V(5) { + glog.Info("doWrite: Remain in txStateSet, txState: ", d.txState) + } + case txStateMultiExec: + glog.Error("doWrite: Incorrect State, txState: ", d.txState) + e = errors.New("Cannot issue {Set|Mod|Delete}Entry in txStateMultiExec") + default: + glog.Error("doWrite: Unknown, txState: ", d.txState) + e = errors.New("Unknown State: " + string(d.txState)) + } + + if e != nil { + goto doWriteExit + } + + // No Transaction case. No CVL. + if d.txState == txStateNone { + + switch op { + + case txOpHMSet: + value = Value { Field: make(map[string]string, + len(val.(Value).Field)) } + vintf := make(map[string]interface{}) + for k, v := range val.(Value).Field { + vintf[k] = v + } + e = d.client.HMSet(d.key2redis(ts, key), vintf).Err() + + if e!= nil { + glog.Error("doWrite: HMSet: ", key, " : ", value, " e: ", e) + } + + case txOpHDel: + fields := make([]string, 0, len(val.(Value).Field)) + for k, _ := range val.(Value).Field { + fields = append(fields, k) + } + + e = d.client.HDel(d.key2redis(ts, key), fields...).Err() + if e!= nil { + glog.Error("doWrite: HDel: ", key, " : ", fields, " e: ", e) + } + + case txOpDel: + e = d.client.Del(d.key2redis(ts, key)).Err() + if e!= nil { + glog.Error("doWrite: Del: ", key, " : ", e) + } + + default: + glog.Error("doWrite: Unknown, op: ", op) + e = errors.New("Unknown Op: " + string(op)) + } + + goto doWriteExit + } + + // Transaction case. + + glog.Info("doWrite: op: ", op, " ", key, " : ", value) + + switch op { + case txOpHMSet, txOpHDel: + value = val.(Value) + + case txOpDel: + + default: + glog.Error("doWrite: Unknown, op: ", op) + e = errors.New("Unknown Op: " + string(op)) + } + + if e != nil { + goto doWriteExit + } + + d.txCmds = append(d.txCmds, _txCmd{ + ts: ts, + op: op, + key: &key, + value: &value, + }) + +doWriteExit: + + if glog.V(3) { + glog.Info("doWrite: End: e: ", e) + } + + return e +} + +// setEntry either Creates, or Sets an entry(row) in the table. +func (d *DB) setEntry(ts *TableSpec, key Key, value Value, isCreate bool) error { + + var e error = nil + var valueComplement Value = Value { Field: make(map[string]string,len(value.Field))} + var valueCurrent Value + + if glog.V(3) { + glog.Info("setEntry: Begin: ", "ts: ", ts, " key: ", key, + " value: ", value, " isCreate: ", isCreate) + } + + if len(value.Field) == 0 { + glog.Info("setEntry: Mapping to DeleteEntry()") + e = d.DeleteEntry(ts, key) + goto setEntryExit + } + + if isCreate == false { + // Prepare the HDel list + // Note: This is for compatibililty with PySWSSDK semantics. + // The CVL library will likely fail the SetEntry when + // the item exists. + valueCurrent, e = d.GetEntry(ts, key) + if e == nil { + for k, _ := range valueCurrent.Field { + _, present := value.Field[k] + if ! present { + valueComplement.Field[k] = string("") + } + } + } + } + + if isCreate == false && e == nil { + if glog.V(3) { + glog.Info("setEntry: DoCVL for UPDATE") + } + if len(valueComplement.Field) == 0 { + e = d.doCVL(ts, []cvl.CVLOperation {cvl.OP_UPDATE}, + key, []Value { value} ) + } else { + e = d.doCVL(ts, []cvl.CVLOperation {cvl.OP_UPDATE, cvl.OP_DELETE}, + key, []Value { value, valueComplement} ) + } + } else { + if glog.V(3) { + glog.Info("setEntry: DoCVL for CREATE") + } + e = d.doCVL(ts, []cvl.CVLOperation {cvl.OP_CREATE}, key, []Value { value }) + } + + if e != nil { + goto setEntryExit + } + + e = d.doWrite(ts, txOpHMSet, key, value) + + if (e == nil) && (len(valueComplement.Field) != 0) { + if glog.V(3) { + glog.Info("setEntry: DoCVL for HDEL (post-POC)") + } + e = d.doWrite(ts, txOpHDel, key, valueComplement) + } + +setEntryExit: + return e +} + +// CreateEntry creates an entry(row) in the table. +func (d * DB) CreateEntry(ts * TableSpec, key Key, value Value) error { + + return d.setEntry(ts, key, value, true) +} + +// SetEntry sets an entry(row) in the table. +func (d *DB) SetEntry(ts *TableSpec, key Key, value Value) error { + return d.setEntry(ts, key, value, false) +} + + +func (d* DB) Publish(channel string, message interface{}) error { + e := d.client.Publish(channel, message).Err() + return e +} + +// DeleteEntry deletes an entry(row) in the table. +func (d *DB) DeleteEntry(ts *TableSpec, key Key) error { + + var e error = nil + if glog.V(3) { + glog.Info("DeleteEntry: Begin: ", "ts: ", ts, " key: ", key) + } + + if glog.V(3) { + glog.Info("DeleteEntry: DoCVL for DELETE") + } + e = d.doCVL(ts, []cvl.CVLOperation {cvl.OP_DELETE}, key, []Value {Value{}}) + + if e == nil { + e = d.doWrite(ts, txOpDel, key, nil) + } + + return e; +} + +// ModEntry modifies an entry(row) in the table. +func (d *DB) ModEntry(ts *TableSpec, key Key, value Value) error { + + var e error = nil + + if glog.V(3) { + glog.Info("ModEntry: Begin: ", "ts: ", ts, " key: ", key, + " value: ", value) + } + + if len(value.Field) == 0 { + glog.Info("ModEntry: Mapping to DeleteEntry()") + e = d.DeleteEntry(ts, key) + goto ModEntryExit + } + + if glog.V(3) { + glog.Info("ModEntry: DoCVL for UPDATE") + } + e = d.doCVL(ts, []cvl.CVLOperation {cvl.OP_UPDATE}, key, []Value {value}) + + if e == nil { + e = d.doWrite(ts, txOpHMSet, key, value) + } + +ModEntryExit: + + return e +} + +// DeleteEntryFields deletes some fields/columns in an entry(row) in the table. +func (d *DB) DeleteEntryFields(ts *TableSpec, key Key, value Value) error { + + if glog.V(3) { + glog.Info("DeleteEntryFields: Begin: ", "ts: ", ts, " key: ", key, + " value: ", value) + } + + if glog.V(3) { + glog.Info("DeleteEntryFields: DoCVL for HDEL (post-POC)") + } + + if glog.V(3) { + glog.Info("DeleteEntryFields: DoCVL for HDEL") + } + + e := d.doCVL(ts, []cvl.CVLOperation {cvl.OP_DELETE}, key, []Value{value}) + + if e == nil { + d.doWrite(ts, txOpHDel, key, value) + } + + return e +} + + +// GetTable gets the entire table. +func (d *DB) GetTable(ts *TableSpec) (Table, error) { + if glog.V(3) { + glog.Info("GetTable: Begin: ts: ", ts) + } + + /* + table := Table{ + ts: ts, + entry: map[string]Value{ + "table1|k0.0|k0.1": Value{ + map[string]string{ + "f0.0": "v0.0", + "f0.1": "v0.1", + "f0.2": "v0.2", + }, + }, + "table1|k1.0|k1.1": Value{ + map[string]string{ + "f1.0": "v1.0", + "f1.1": "v1.1", + "f1.2": "v1.2", + }, + }, + }, + db: d, + } + */ + + // Create Table + table := Table{ + ts: ts, + entry: make(map[string]Value), + db: d, + } + + // This can be done via a LUA script as well. For now do this. TBD + // Read Keys + keys, e := d.GetKeys(ts) + if e != nil { + glog.Error("GetTable: GetKeys: " + e.Error()) + goto GetTableExit + } + + // For each key in Keys + // Add Value into table.entry[key)] + for i := 0; i < len(keys); i++ { + value, e := d.GetEntry(ts, keys[i]) + if e != nil { + glog.Warning("GetTable: GetKeys: " + e.Error()) + continue + } + table.entry[d.key2redis(ts, keys[i])] = value + } + +GetTableExit: + + if glog.V(3) { + glog.Info("GetTable: End: table: ", table) + } + return table, e +} + +// DeleteTable deletes the entire table. +func (d *DB) DeleteTable(ts *TableSpec) error { + if glog.V(3) { + glog.Info("DeleteTable: Begin: ts: ", ts) + } + + // This can be done via a LUA script as well. For now do this. TBD + // Read Keys + keys, e := d.GetKeys(ts) + if e != nil { + glog.Error("DeleteTable: GetKeys: " + e.Error()) + goto DeleteTableExit + } + + // For each key in Keys + // Delete the entry + for i := 0; i < len(keys); i++ { + e := d.DeleteEntry(ts, keys[i]) + if e != nil { + glog.Warning("DeleteTable: DeleteEntry: " + e.Error()) + break + } + } +DeleteTableExit: + if glog.V(3) { + glog.Info("DeleteTable: End: ") + } + return e +} + +// GetKeys method retrieves all entry/row keys from a previously read table. +func (t *Table) GetKeys() ([]Key, error) { + if glog.V(3) { + glog.Info("Table.GetKeys: Begin: t: ", t) + } + keys := make([]Key, 0, len(t.entry)) + for k, _ := range t.entry { + keys = append(keys, t.db.redis2key(t.ts, k)) + } + + if glog.V(3) { + glog.Info("Table.GetKeys: End: keys: ", keys) + } + return keys, nil +} + +// GetEntry method retrieves an entry/row from a previously read table. +func (t *Table) GetEntry(key Key) (Value, error) { + /* + return Value{map[string]string{ + "f0.0": "v0.0", + "f0.1": "v0.1", + "f0.2": "v0.2", + }, + }, nil + */ + if glog.V(3) { + glog.Info("Table.GetEntry: Begin: t: ", t, " key: ", key) + } + v := t.entry[t.db.key2redis(t.ts, key)] + if glog.V(3) { + glog.Info("Table.GetEntry: End: entry: ", v) + } + return v, nil +} + +//===== Functions for db.Key ===== + +// Len returns number of components in the Key +func (k *Key) Len() int { + return len(k.Comp) +} + +// Get returns the key component at given index +func (k *Key) Get(index int) string { + return k.Comp[index] +} + +//===== Functions for db.Value ===== + +func (v *Value) IsPopulated() bool { + return len(v.Field) > 0 +} + +// Has function checks if a field exists. +func (v *Value) Has(name string) bool { + _, flag := v.Field[name] + return flag +} + +// Get returns the value of a field. Returns empty string if the field +// does not exists. Use Has() function to check existance of field. +func (v *Value) Get(name string) string { + return v.Field[name] +} + +// Set function sets a string value for a field. +func (v *Value) Set(name, value string) { + v.Field[name] = value +} + +// GetInt returns value of a field as int. Returns 0 if the field does +// not exists. Returns an error if the field value is not a number. +func (v *Value) GetInt(name string) (int, error) { + data, ok := v.Field[name] + if ok { + return strconv.Atoi(data) + } + return 0, nil +} + +// SetInt sets an integer value for a field. +func (v *Value) SetInt(name string, value int) { + v.Set(name, strconv.Itoa(value)) +} + +// GetList returns the value of a an array field. A "@" suffix is +// automatically appended to the field name if not present (as per +// swsssdk convention). Field value is split by comma and resulting +// slice is returned. Empty slice is returned if field not exists. +func (v *Value) GetList(name string) []string { + var data string + if strings.HasSuffix(name, "@") { + data = v.Get(name) + } else { + data = v.Get(name + "@") + } + + if len(data) == 0 { + return []string{} + } + + return strings.Split(data, ",") +} + +// SetList function sets an list value to a field. Field name and +// value are formatted as per swsssdk conventions: +// - A "@" suffix is appended to key name +// - Field value is the comma separated string of list items +func (v *Value) SetList(name string, items []string) { + if !strings.HasSuffix(name, "@") { + name += "@" + } + + if len(items) != 0 { + data := strings.Join(items, ",") + v.Set(name, data) + } else { + v.Remove(name) + } +} + +// Remove function removes a field from this Value. +func (v *Value) Remove(name string) { + delete(v.Field, name) +} + +////////////////////////////////////////////////////////////////////////// +// The Transaction API for translib infra +////////////////////////////////////////////////////////////////////////// + +// WatchKeys is array of (TableSpec, Key) tuples to be watched in a Transaction. +type WatchKeys struct { + Ts *TableSpec + Key *Key +} + +func (w WatchKeys) String() string { + return fmt.Sprintf("{ Ts: %v, Key: %v }", w.Ts, w.Key) +} + +// Convenience function to make TableSpecs from strings. +// This only works on Tables having key components without TableSeparator +// as part of the key. +func Tables2TableSpecs(tables []string) []* TableSpec { + var tss []*TableSpec + + tss = make([]*TableSpec, 0, len(tables)) + + for i := 0; i < len(tables); i++ { + tss = append(tss, &(TableSpec{ Name: tables[i]})) + } + + return tss +} + +// StartTx method is used by infra to start a check-and-set Transaction. +func (d *DB) StartTx(w []WatchKeys, tss []*TableSpec) error { + + if glog.V(3) { + glog.Info("StartTx: Begin: w: ", w, " tss: ", tss) + } + + var e error = nil + var ret cvl.CVLRetCode + + //Start CVL session + if d.cv, ret = cvl.ValidationSessOpen(); ret != cvl.CVL_SUCCESS { + e = errors.New("StartTx: Unable to create CVL session") + goto StartTxExit + } + + // Validate State + if d.txState != txStateNone { + glog.Error("StartTx: Incorrect State, txState: ", d.txState) + e = errors.New("Transaction already in progress") + goto StartTxExit + } + + e = d.performWatch(w, tss) + +StartTxExit: + + if glog.V(3) { + glog.Info("StartTx: End: e: ", e) + } + return e +} + +func (d *DB) AppendWatchTx(w []WatchKeys, tss []*TableSpec) error { + if glog.V(3) { + glog.Info("AppendWatchTx: Begin: w: ", w, " tss: ", tss) + } + + var e error = nil + + // Validate State + if d.txState == txStateNone { + glog.Error("AppendWatchTx: Incorrect State, txState: ", d.txState) + e = errors.New("Transaction has not started") + goto AppendWatchTxExit + } + + e = d.performWatch(w, tss) + +AppendWatchTxExit: + + if glog.V(3) { + glog.Info("AppendWatchTx: End: e: ", e) + } + return e +} + +func (d *DB) performWatch(w []WatchKeys, tss []*TableSpec) error { + var e error + var args []interface{} + + // For each watchkey + // If a pattern, Get the keys, appending results to Cmd args. + // Else append keys to the Cmd args + // Note: (LUA scripts do not support WATCH) + + args = make([]interface{}, 0, len(w) + len(tss) + 1) + args = append(args, "WATCH") + for i := 0; i < len(w); i++ { + + redisKey := d.key2redis(w[i].Ts, *(w[i].Key)) + + if !strings.Contains(redisKey, "*") { + args = append(args, redisKey) + continue + } + + redisKeys, e := d.client.Keys(redisKey).Result() + if e != nil { + glog.Warning("performWatch: Keys: " + e.Error()) + continue + } + for j := 0; j < len(redisKeys); j++ { + args = append(args, d.redis2key(w[i].Ts, redisKeys[j])) + } + } + + // for each TS, append to args the CONFIG_DB_UPDATED_ key + + for i := 0; i < len(tss); i++ { + args = append( args, d.ts2redisUpdated(tss[i])) + } + + if len(args) == 1 { + glog.Warning("performWatch: Empty WatchKeys. Skipping WATCH") + goto SkipWatch + } + + // Issue the WATCH + _, e = d.client.Do(args...).Result() + + if e != nil { + glog.Warning("performWatch: Do: WATCH ", args, " e: ", e.Error()) + } + +SkipWatch: + + // Switch State + d.txState = txStateWatch + + return e +} + +// CommitTx method is used by infra to commit a check-and-set Transaction. +func (d *DB) CommitTx() error { + if glog.V(3) { + glog.Info("CommitTx: Begin:") + } + + var e error = nil + var tsmap map[TableSpec]bool = + make(map[TableSpec]bool, len(d.txCmds)) // UpperBound + + // Validate State + switch d.txState { + case txStateNone: + glog.Error("CommitTx: No WATCH done, txState: ", d.txState) + e = errors.New("StartTx() not done. No Transaction active.") + case txStateWatch: + if glog.V(1) { + glog.Info("CommitTx: No SET|DEL done, txState: ", d.txState) + } + case txStateSet: + break + case txStateMultiExec: + glog.Error("CommitTx: Incorrect State, txState: ", d.txState) + e = errors.New("Cannot issue MULTI in txStateMultiExec") + default: + glog.Error("CommitTx: Unknown, txState: ", d.txState) + e = errors.New("Unknown State: " + string(d.txState)) + } + + if e != nil { + goto CommitTxExit + } + + // Issue MULTI + _, e = d.client.Do("MULTI").Result() + + if e != nil { + glog.Warning("CommitTx: Do: MULTI e: ", e.Error()) + } + + // For each cmd in txCmds + // Invoke it + for i := 0; i < len(d.txCmds); i++ { + + var args []interface{} + + redisKey := d.key2redis(d.txCmds[i].ts, *(d.txCmds[i].key)) + + // Add TS to the map of watchTables + tsmap[*(d.txCmds[i].ts)] = true; + + switch d.txCmds[i].op { + + case txOpHMSet: + + args = make([]interface{}, 0, len(d.txCmds[i].value.Field)*2+2) + args = append(args, "HMSET", redisKey) + + for k, v := range d.txCmds[i].value.Field { + args = append(args, k, v) + } + + if glog.V(4) { + glog.Info("CommitTx: Do: ", args) + } + + _, e = d.client.Do(args...).Result() + + case txOpHDel: + + args = make([]interface{}, 0, len(d.txCmds[i].value.Field)+2) + args = append(args, "HDEL", redisKey) + + for k, _ := range d.txCmds[i].value.Field { + args = append(args, k) + } + + if glog.V(4) { + glog.Info("CommitTx: Do: ", args) + } + + _, e = d.client.Do(args...).Result() + + case txOpDel: + + args = make([]interface{}, 0, 2) + args = append(args, "DEL", redisKey) + + if glog.V(4) { + glog.Info("CommitTx: Do: ", args) + } + + _, e = d.client.Do(args...).Result() + + default: + glog.Error("CommitTx: Unknown, op: ", d.txCmds[i].op) + e = errors.New("Unknown Op: " + string(d.txCmds[i].op)) + } + + if e != nil { + glog.Warning("CommitTx: Do: ", args, " e: ", e.Error()) + } + } + + // Flag the Tables as updated. + for ts, _ := range tsmap { + _, e = d.client.Do("SET", d.ts2redisUpdated(&ts), "1").Result() + if e != nil { + glog.Warning("CommitTx: Do: SET ", + d.ts2redisUpdated(&ts), " 1: e: ", + e.Error()) + } + } + _, e = d.client.Do("SET", d.ts2redisUpdated(& TableSpec{Name: "*"}), + "1").Result() + if e != nil { + glog.Warning("CommitTx: Do: SET ", + "CONFIG_DB_UPDATED", " 1: e: ", e.Error()) + } + + // Issue EXEC + _, e = d.client.Do("EXEC").Result() + + if e != nil { + glog.Warning("CommitTx: Do: EXEC e: ", e.Error()) + e = tlerr.TranslibTransactionFail { } + } + + // Switch State, Clear Command list + d.txState = txStateNone + d.txCmds = d.txCmds[:0] + d.cvlEditConfigData = d.cvlEditConfigData[:0] + + //Close CVL session + if ret := cvl.ValidationSessClose(d.cv); ret != cvl.CVL_SUCCESS { + glog.Error("CommitTx: End: Error in closing CVL session") + } + d.cv = nil + +CommitTxExit: + if glog.V(3) { + glog.Info("CommitTx: End: e: ", e) + } + return e +} + +// AbortTx method is used by infra to abort a check-and-set Transaction. +func (d *DB) AbortTx() error { + if glog.V(3) { + glog.Info("AbortTx: Begin:") + } + + var e error = nil + + // Validate State + switch d.txState { + case txStateNone: + glog.Error("AbortTx: No WATCH done, txState: ", d.txState) + e = errors.New("StartTx() not done. No Transaction active.") + case txStateWatch: + if glog.V(1) { + glog.Info("AbortTx: No SET|DEL done, txState: ", d.txState) + } + case txStateSet: + break + case txStateMultiExec: + glog.Error("AbortTx: Incorrect State, txState: ", d.txState) + e = errors.New("Cannot issue UNWATCH in txStateMultiExec") + default: + glog.Error("AbortTx: Unknown, txState: ", d.txState) + e = errors.New("Unknown State: " + string(d.txState)) + } + + if e != nil { + goto AbortTxExit + } + + // Issue UNWATCH + _, e = d.client.Do("UNWATCH").Result() + + if e != nil { + glog.Warning("AbortTx: Do: UNWATCH e: ", e.Error()) + } + + // Switch State, Clear Command list + d.txState = txStateNone + d.txCmds = d.txCmds[:0] + d.cvlEditConfigData = d.cvlEditConfigData[:0] + + //Close CVL session + if ret := cvl.ValidationSessClose(d.cv); ret != cvl.CVL_SUCCESS { + glog.Error("AbortTx: End: Error in closing CVL session") + } + d.cv = nil + +AbortTxExit: + if glog.V(3) { + glog.Info("AbortTx: End: e: ", e) + } + return e +} diff --git a/src/translib/db/db_test.go b/src/translib/db/db_test.go new file mode 100644 index 0000000000..edf05b281c --- /dev/null +++ b/src/translib/db/db_test.go @@ -0,0 +1,610 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 + + +import ( + // "fmt" + // "errors" + // "flag" + // "github.com/golang/glog" + "time" + // "translib/tlerr" + // "os/exec" + "os" + "testing" + "strconv" + "reflect" +) + +func TestMain(m * testing.M) { + + exitCode := 0 + +/* Apparently, on an actual switch the swss container will have + * a redis-server running, which will be in a different container than + * mgmt, thus this pkill stuff to find out it is running will not work. + * + + redisServerAttemptedStart := false + +TestMainRedo: + o, e := exec.Command("/usr/bin/pkill", "-HUP", "redis-server").Output() + + if e == nil { + + } else if redisServerAttemptedStart { + + exitCode = 1 + + } else { + + fmt.Printf("TestMain: No redis server: pkill: %v\n", o) + fmt.Println("TestMain: Starting redis-server") + e = exec.Command("/tools/bin/redis-server").Start() + time.Sleep(3 * time.Second) + redisServerAttemptedStart = true + goto TestMainRedo + } +*/ + + if exitCode == 0 { + exitCode = m.Run() + } + + + os.Exit(exitCode) + +} + +/* + +1. Create, and close a DB connection. (NewDB(), DeleteDB()) + +*/ + +func TestNewDB(t * testing.T) { + + d,e := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + if d == nil { + t.Errorf("NewDB() fails e = %v", e) + } else if e = d.DeleteDB() ; e != nil { + t.Errorf("DeleteDB() fails e = %v", e) + } +} + + +/* + +2. Get an entry (GetEntry()) +3. Set an entry without Transaction (SetEntry()) +4. Delete an entry without Transaction (DeleteEntry()) + +20. NT: GetEntry() EntryNotExist. + +*/ + +func TestNoTransaction(t * testing.T) { + + var pid int = os.Getpid() + + d,e := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + if d == nil { + t.Errorf("NewDB() fails e = %v", e) + return + } + + ts := TableSpec { Name: "TEST_" + strconv.FormatInt(int64(pid), 10) } + + ca := make([]string, 1, 1) + ca[0] = "MyACL1_ACL_IPVNOTEXIST" + akey := Key { Comp: ca} + avalue := Value { map[string]string {"ports@":"Ethernet0","type":"MIRROR" }} + e = d.SetEntry(&ts, akey, avalue) + + if e != nil { + t.Errorf("SetEntry() fails e = %v", e) + return + } + + v, e := d.GetEntry(&ts, akey) + + if (e != nil) || (!reflect.DeepEqual(v,avalue)) { + t.Errorf("GetEntry() fails e = %v", e) + return + } + + e = d.DeleteEntry(&ts, akey) + + if e != nil { + t.Errorf("DeleteEntry() fails e = %v", e) + return + } + + v, e = d.GetEntry(&ts, akey) + + if e == nil { + t.Errorf("GetEntry() after DeleteEntry() fails e = %v", e) + return + } + + if e = d.DeleteDB() ; e != nil { + t.Errorf("DeleteDB() fails e = %v", e) + } +} + + +/* + +5. Get a Table (GetTable()) + +9. Get multiple keys (GetKeys()) +10. Delete multiple keys (DeleteKeys()) +11. Delete Table (DeleteTable()) + +*/ + +func TestTable(t * testing.T) { + + var pid int = os.Getpid() + + d,e := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + if d == nil { + t.Errorf("NewDB() fails e = %v", e) + return + } + + ts := TableSpec { Name: "TEST_" + strconv.FormatInt(int64(pid), 10) } + + ca := make([]string, 1, 1) + ca[0] = "MyACL1_ACL_IPVNOTEXIST" + akey := Key { Comp: ca} + avalue := Value { map[string]string {"ports@":"Ethernet0","type":"MIRROR" }} + ca2 := make([]string, 1, 1) + ca2[0] = "MyACL2_ACL_IPVNOTEXIST" + akey2 := Key { Comp: ca2} + + // Add the Entries for Get|DeleteKeys + + e = d.SetEntry(&ts, akey, avalue) + + if e != nil { + t.Errorf("SetEntry() fails e = %v", e) + return + } + + e = d.SetEntry(&ts, akey2, avalue) + + if e != nil { + t.Errorf("SetEntry() fails e = %v", e) + return + } + + keys, e := d.GetKeys(&ts) + + if (e != nil) || (len(keys) != 2) { + t.Errorf("GetKeys() fails e = %v", e) + return + } + + e = d.DeleteKeys(&ts, Key {Comp: []string {"MyACL*_ACL_IPVNOTEXIST"}}) + + if e != nil { + t.Errorf("DeleteKeys() fails e = %v", e) + return + } + + v, e := d.GetEntry(&ts, akey) + + if e == nil { + t.Errorf("GetEntry() after DeleteKeys() fails e = %v", e) + return + } + + + + // Add the Entries again for Table + + e = d.SetEntry(&ts, akey, avalue) + + if e != nil { + t.Errorf("SetEntry() fails e = %v", e) + return + } + + e = d.SetEntry(&ts, akey2, avalue) + + if e != nil { + t.Errorf("SetEntry() fails e = %v", e) + return + } + + tab, e := d.GetTable(&ts) + + if e != nil { + t.Errorf("GetTable() fails e = %v", e) + return + } + + v, e = tab.GetEntry(akey) + + if (e != nil) || (!reflect.DeepEqual(v,avalue)) { + t.Errorf("Table.GetEntry() fails e = %v", e) + return + } + + e = d.DeleteTable(&ts) + + if e != nil { + t.Errorf("DeleteTable() fails e = %v", e) + return + } + + v, e = d.GetEntry(&ts, akey) + + if e == nil { + t.Errorf("GetEntry() after DeleteTable() fails e = %v", e) + return + } + + if e = d.DeleteDB() ; e != nil { + t.Errorf("DeleteDB() fails e = %v", e) + } +} + + +/* Tests for + +6. Set an entry with Transaction (StartTx(), SetEntry(), CommitTx()) +7. Delete an entry with Transaction (StartTx(), DeleteEntry(), CommitTx()) +8. Abort Transaction. (StartTx(), DeleteEntry(), AbortTx()) + +12. Set an entry with Transaction using WatchKeys Check-And-Set(CAS) +13. Set an entry with Transaction using Table CAS +14. Set an entry with Transaction using WatchKeys, and Table CAS + +15. Set an entry with Transaction with empty WatchKeys, and Table CAS +16. Negative Test(NT): Fail a Transaction using WatchKeys CAS +17. NT: Fail a Transaction using Table CAS +18. NT: Abort an Transaction with empty WatchKeys/Table CAS + +Cannot Automate 19 for now +19. NT: Check V logs, Error logs + + */ + +func TestTransaction(t * testing.T) { + for transRun := TransRunBasic ; transRun < TransRunEnd ; transRun++ { + testTransaction(t, transRun) + } +} + +type TransRun int + +const ( + TransRunBasic TransRun = iota // 0 + TransRunWatchKeys // 1 + TransRunTable // 2 + TransRunWatchKeysAndTable // 3 + TransRunEmptyWatchKeysAndTable // 4 + TransRunFailWatchKeys // 5 + TransRunFailTable // 6 + + // Nothing after this. + TransRunEnd +) + +func testTransaction(t * testing.T, transRun TransRun) { + + var pid int = os.Getpid() + + d,e := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + if d == nil { + t.Errorf("NewDB() fails e = %v, transRun = %v", e, transRun) + return + } + + ts := TableSpec { Name: "TEST_" + strconv.FormatInt(int64(pid), 10) } + + ca := make([]string, 1, 1) + ca[0] = "MyACL1_ACL_IPVNOTEXIST" + akey := Key { Comp: ca} + avalue := Value { map[string]string {"ports@":"Ethernet0","type":"MIRROR" }} + + var watchKeys []WatchKeys + var table []*TableSpec + + switch transRun { + case TransRunBasic, TransRunWatchKeysAndTable: + watchKeys = []WatchKeys{{Ts: &ts, Key: &akey}} + table = []*TableSpec { &ts } + case TransRunWatchKeys, TransRunFailWatchKeys: + watchKeys = []WatchKeys{{Ts: &ts, Key: &akey}} + table = []*TableSpec { } + case TransRunTable, TransRunFailTable: + watchKeys = []WatchKeys{} + table = []*TableSpec { &ts } + } + + e = d.StartTx(watchKeys, table) + + if e != nil { + t.Errorf("StartTx() fails e = %v", e) + return + } + + e = d.SetEntry(&ts, akey, avalue) + + if e != nil { + t.Errorf("SetEntry() fails e = %v", e) + return + } + + e = d.CommitTx() + + if e != nil { + t.Errorf("CommitTx() fails e = %v", e) + return + } + + v, e := d.GetEntry(&ts, akey) + + if (e != nil) || (!reflect.DeepEqual(v,avalue)) { + t.Errorf("GetEntry() after Tx fails e = %v", e) + return + } + + e = d.StartTx(watchKeys, table) + + if e != nil { + t.Errorf("StartTx() fails e = %v", e) + return + } + + e = d.DeleteEntry(&ts, akey) + + if e != nil { + t.Errorf("DeleteEntry() fails e = %v", e) + return + } + + e = d.AbortTx() + + if e != nil { + t.Errorf("AbortTx() fails e = %v", e) + return + } + + v, e = d.GetEntry(&ts, akey) + + if (e != nil) || (!reflect.DeepEqual(v,avalue)) { + t.Errorf("GetEntry() after Abort Tx fails e = %v", e) + return + } + + e = d.StartTx(watchKeys, table) + + if e != nil { + t.Errorf("StartTx() fails e = %v", e) + return + } + + e = d.DeleteEntry(&ts, akey) + + if e != nil { + t.Errorf("DeleteEntry() fails e = %v", e) + return + } + + switch transRun { + case TransRunFailWatchKeys, TransRunFailTable: + d2,_ := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + d2.StartTx(watchKeys, table); + d2.DeleteEntry(&ts, akey) + d2.CommitTx(); + d2.DeleteDB(); + default: + } + + e = d.CommitTx() + + switch transRun { + case TransRunFailWatchKeys, TransRunFailTable: + if e == nil { + t.Errorf("NT CommitTx() tr: %v fails e = %v", + transRun, e) + return + } + default: + if e != nil { + t.Errorf("CommitTx() fails e = %v", e) + return + } + } + + v, e = d.GetEntry(&ts, akey) + + if e == nil { + t.Errorf("GetEntry() after Tx DeleteEntry() fails e = %v", e) + return + } + + d.DeleteMapAll(&ts) + + if e = d.DeleteDB() ; e != nil { + t.Errorf("DeleteDB() fails e = %v", e) + } +} + + +func TestMap(t * testing.T) { + + var pid int = os.Getpid() + + d,e := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + if d == nil { + t.Errorf("NewDB() fails e = %v", e) + return + } + + ts := TableSpec { Name: "TESTMAP_" + strconv.FormatInt(int64(pid), 10) } + + d.SetMap(&ts, "k1", "v1"); + d.SetMap(&ts, "k2", "v2"); + + if v, e := d.GetMap(&ts, "k1"); v != "v1" { + t.Errorf("GetMap() fails e = %v", e) + return + } + + if v, e := d.GetMapAll(&ts) ; + (e != nil) || + (!reflect.DeepEqual(v, + Value{ Field: map[string]string { + "k1" : "v1", "k2" : "v2" }})) { + t.Errorf("GetMapAll() fails e = %v", e) + return + } + + d.DeleteMapAll(&ts) + + if e = d.DeleteDB() ; e != nil { + t.Errorf("DeleteDB() fails e = %v", e) + } +} + +func TestSubscribe(t * testing.T) { + + var pid int = os.Getpid() + + var hSetCalled, hDelCalled, delCalled bool + + d,e := NewDB(Options { + DBNo : ConfigDB, + InitIndicator : "", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }) + + if (d == nil) || (e != nil) { + t.Errorf("NewDB() fails e = %v", e) + return + } + + ts := TableSpec { Name: "TEST_" + strconv.FormatInt(int64(pid), 10) } + + ca := make([]string, 1, 1) + ca[0] = "MyACL1_ACL_IPVNOTEXIST" + akey := Key { Comp: ca} + avalue := Value { map[string]string {"ports@":"Ethernet0","type":"MIRROR" }} + + var skeys [] *SKey = make([]*SKey, 1) + skeys[0] = & (SKey { Ts: &ts, Key: &akey, + SEMap: map[SEvent]bool { + SEventHSet: true, + SEventHDel: true, + SEventDel: true, + }}) + + s,e := SubscribeDB(Options { + DBNo : ConfigDB, + InitIndicator : "CONFIG_DB_INITIALIZED", + TableNameSeparator: "|", + KeySeparator : "|", + DisableCVLCheck : true, + }, skeys, func (s *DB, + skey *SKey, key *Key, + event SEvent) error { + switch event { + case SEventHSet: hSetCalled = true + case SEventHDel: hDelCalled = true + case SEventDel: delCalled = true + default: + } + return nil }) + + if (s == nil) || (e != nil) { + t.Errorf("Subscribe() returns error e: %v", e) + return + } + + d.SetEntry(&ts, akey, avalue) + d.DeleteEntryFields(&ts, akey, avalue) + + time.Sleep(5 * time.Second) + + if !hSetCalled || !hDelCalled || !delCalled { + t.Errorf("Subscribe() callbacks missed: %v %v %v", hSetCalled, + hDelCalled, delCalled) + return + } + + s.UnsubscribeDB() + + time.Sleep(2 * time.Second) + + if e = d.DeleteDB() ; e != nil { + t.Errorf("DeleteDB() fails e = %v", e) + } +} + diff --git a/src/translib/db/map.go b/src/translib/db/map.go new file mode 100644 index 0000000000..9010cfb15f --- /dev/null +++ b/src/translib/db/map.go @@ -0,0 +1,123 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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. +*/ +package db + +import ( + // "fmt" + // "strconv" + + // "reflect" + // "errors" + // "strings" + + // "github.com/go-redis/redis" + "github.com/golang/glog" + // "cvl" + "translib/tlerr" +) + +func init() { +} + + + + +func (d *DB) GetMap(ts *TableSpec, mapKey string) (string, error) { + + if glog.V(3) { + glog.Info("GetMap: Begin: ", "ts: ", ts, " mapKey: ", mapKey) + } + + v, e := d.client.HGet(ts.Name, mapKey).Result() + + if glog.V(3) { + glog.Info("GetMap: End: ", "v: ", v, " e: ", e) + } + + return v, e +} + +func (d *DB) GetMapAll(ts *TableSpec) (Value, error) { + + if glog.V(3) { + glog.Info("GetMapAll: Begin: ", "ts: ", ts) + } + + var value Value + + v, e := d.client.HGetAll(ts.Name).Result() + + if len(v) != 0 { + value = Value{Field: v} + } else { + if glog.V(1) { + glog.Info("GetMapAll: HGetAll(): empty map") + } + e = tlerr.TranslibRedisClientEntryNotExist { Entry: ts.Name } + } + + if glog.V(3) { + glog.Info("GetMapAll: End: ", "value: ", value, " e: ", e) + } + + return value, e +} + +// For Testing only. Do Not Use!!! ============================== +// There is no transaction support on these. +func (d *DB) SetMap(ts *TableSpec, mapKey string, mapValue string) error { + + if glog.V(3) { + glog.Info("SetMap: Begin: ", "ts: ", ts, " ", mapKey, + ":", mapValue) + } + + b, e := d.client.HSet(ts.Name, mapKey, mapValue).Result() + + if glog.V(3) { + glog.Info("GetMap: End: ", "b: ", b, " e: ", e) + } + + return e +} +// For Testing only. Do Not Use!!! ============================== + +// For Testing only. Do Not Use!!! +// There is no transaction support on these. +func (d *DB) DeleteMapAll(ts *TableSpec) error { + + if glog.V(3) { + glog.Info("DeleteMapAll: Begin: ", "ts: ", ts) + } + + e := d.client.Del(ts.Name).Err() + + if glog.V(3) { + glog.Info("DeleteMapAll: End: ", " e: ", e) + } + + return e +} +// For Testing only. Do Not Use!!! ============================== + + diff --git a/src/translib/db/subscribe.go b/src/translib/db/subscribe.go new file mode 100644 index 0000000000..65ea09811e --- /dev/null +++ b/src/translib/db/subscribe.go @@ -0,0 +1,275 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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. +*/ +package db + +import ( + // "fmt" + // "strconv" + + // "reflect" + "errors" + "strings" + + // "github.com/go-redis/redis" + "github.com/golang/glog" + // "cvl" + "translib/tlerr" +) + +// SKey is (TableSpec, Key, []SEvent) 3-tuples to be watched in a Transaction. +type SKey struct { + Ts *TableSpec + Key *Key + SEMap map[SEvent]bool // nil map indicates subscribe to all +} + +type SEvent int + +const ( + SEventNone SEvent = iota // No Op + SEventHSet // HSET, HMSET, and its variants + SEventHDel // HDEL, also SEventDel generated, if HASH is becomes empty + SEventDel // DEL, & also if key gets deleted (empty HASH, expire,..) + SEventOther // Some other command not covered above. + + // The below two are always sent regardless of SEMap. + SEventClose // Close requested due to Unsubscribe() called. + SEventErr // Error condition. Call Unsubscribe, after return. +) + +var redisPayload2sEventMap map[string]SEvent = map[string]SEvent { + "" : SEventNone, + "hset" : SEventHSet, + "hdel" : SEventHDel, + "del" : SEventDel, +} + + +func init() { + // Optimization: Start the goroutine that is scanning the SubscribeDB + // channels. Instead of one goroutine per Subscribe. +} + + +// HFunc gives the name of the table, and other per-table customizations. +type HFunc func( *DB, *SKey, *Key, SEvent) (error) + + +// SubscribeDB is the factory method to create a subscription to the DB. +// The returned instance can only be used for Subscription. +func SubscribeDB(opt Options, skeys []*SKey, handler HFunc) (*DB, error) { + + if glog.V(3) { + glog.Info("SubscribeDB: Begin: opt: ", opt, + " skeys: ", skeys, " handler: ", handler) + } + + patterns := make([]string, 0, len(skeys)) + patMap := make(map[string]([]int), len(skeys)) + var s string + + // NewDB + d , e := NewDB(opt) + + if d.client == nil { + goto SubscribeDBExit + } + + // Make sure that the DB is configured for key space notifications + // Optimize with LUA scripts to atomically add "Kgshxe". + s, e = d.client.ConfigSet("notify-keyspace-events", "AKE").Result() + + if e != nil { + glog.Error("SubscribeDB: ConfigSet(): e: ", e, " s: ", s) + goto SubscribeDBExit + } + + for i := 0 ; i < len(skeys); i++ { + pattern := d.key2redisChannel(skeys[i].Ts, *(skeys[i].Key)) + if _,present := patMap[pattern] ; ! present { + patMap[pattern] = make([]int, 0, 5) + patterns = append(patterns, pattern) + } + patMap[pattern] = append(patMap[pattern], i) + + } + + glog.Info("SubscribeDB: patterns: ", patterns) + + d.sPubSub = d.client.PSubscribe(patterns[:]...) + + if d.sPubSub == nil { + glog.Error("SubscribeDB: PSubscribe() nil: pats: ", patterns) + e = tlerr.TranslibDBSubscribeFail { } + goto SubscribeDBExit + } + + // Wait for confirmation, of channel creation + _, e = d.sPubSub.Receive() + + if e != nil { + glog.Error("SubscribeDB: Receive() fails: e: ", e) + e = tlerr.TranslibDBSubscribeFail { } + goto SubscribeDBExit + } + + + // Start a goroutine to read messages and call handler. + go func() { + for msg := range d.sPubSub.Channel() { + if glog.V(4) { + glog.Info("SubscribeDB: msg: ", msg) + } + + // Should this be a goroutine, in case each notification CB + // takes a long time to run ? + for _, skeyIndex := range patMap[msg.Pattern] { + skey := skeys[skeyIndex] + key := d.redisChannel2key(skey.Ts, msg.Channel) + sevent := d.redisPayload2sEvent(msg.Payload) + + if len(skey.SEMap) == 0 || skey.SEMap[sevent] { + + if glog.V(2) { + glog.Info("SubscribeDB: handler( ", + &d, ", ", skey, ", ", key, ", ", sevent, " )") + } + + handler(d, skey, &key, sevent) + } + } + } + + // Send the Close|Err notification. + var sEvent = SEventClose + if d.sCIP == false { + sEvent = SEventErr + } + glog.Info("SubscribeDB: SEventClose|Err: ", sEvent) + handler(d, & SKey{}, & Key {}, sEvent) + } () + + +SubscribeDBExit: + + if e != nil { + if d.sPubSub != nil { + d.sPubSub.Close() + } + + if d.client != nil { + d.DeleteDB() + d.client = nil + } + d = nil + } + + if glog.V(3) { + glog.Info("SubscribeDB: End: d: ", d, " e: ", e) + } + + return d, e +} + +// UnsubscribeDB is used to close a DB subscription +func (d * DB) UnsubscribeDB() error { + + var e error = nil + + if glog.V(3) { + glog.Info("UnsubscribeDB: d:", d) + } + + if d.sCIP { + glog.Error("UnsubscribeDB: Close in Progress") + e = errors.New("UnsubscribeDB: Close in Progress") + goto UnsubscribeDBExit + } + + // Mark close in progress. + d.sCIP = true; + + // Do the close, ch gets closed too. + d.sPubSub.Close() + + // Wait for the goroutine to complete ? TBD + // Should not this happen because of the range statement on ch? + + // Close the DB + d.DeleteDB() + +UnsubscribeDBExit: + + if glog.V(3) { + glog.Info("UnsubscribeDB: End: d: ", d, " e: ", e) + } + + return e +} + + +func (d *DB) key2redisChannel(ts *TableSpec, key Key) string { + + if glog.V(5) { + glog.Info("key2redisChannel: ", *ts, " key: " + key.String()) + } + + return "__keyspace@" + (d.Opts.DBNo).String() + "__:" + d.key2redis(ts, key) +} + +func (d *DB) redisChannel2key(ts *TableSpec, redisChannel string) Key { + + if glog.V(5) { + glog.Info("redisChannel2key: ", *ts, " redisChannel: " + redisChannel) + } + + splitRedisKey := strings.SplitN(redisChannel, ":", 2) + + if len(splitRedisKey) > 1 { + return d.redis2key(ts, splitRedisKey[1]) + } + + glog.Warning("redisChannel2key: Missing key: redisChannel: ", redisChannel) + + return Key{} +} + +func (d *DB) redisPayload2sEvent(redisPayload string) SEvent { + + if glog.V(5) { + glog.Info("redisPayload2sEvent: ", redisPayload) + } + + sEvent := redisPayload2sEventMap[redisPayload] + + if sEvent == 0 { + sEvent = SEventOther + } + + if glog.V(3) { + glog.Info("redisPayload2sEvent: ", sEvent) + } + + return sEvent +} + diff --git a/src/translib/db/test/arloIssue29.go b/src/translib/db/test/arloIssue29.go new file mode 100644 index 0000000000..d42613d664 --- /dev/null +++ b/src/translib/db/test/arloIssue29.go @@ -0,0 +1,85 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 +*/ + +package main + +import ( + "fmt" + // "errors" + "flag" + "github.com/golang/glog" + "translib/db" + // "time" + // "translib/tlerr" +) + +func main() { + var avalue db.Value + var akey db.Key + var e error + + defer glog.Flush() + + flag.Parse() + + fmt.Println("https://github.com/project-arlo/sonic-mgmt-framework/issues/29") + fmt.Println("Creating the DB ==============") + d,_ := db.NewDB(db.Options { + DBNo : db.ApplDB, + InitIndicator : "", + TableNameSeparator: ":", + KeySeparator : ":", + }) + + tsi := db.TableSpec { Name: "INTF_TABLE", CompCt: 2 } + + ca := make([]string, 2, 2) + + fmt.Println("Testing SetEntry ==============") + ca[0] = "Ethernet20" + ca[1] = "a::b/64" + akey = db.Key { Comp: ca} + avalue = db.Value { Field: map[string]string { + "scope" : "global", + "family" : "IPv4", + } } + + e = d.SetEntry(&tsi, akey, avalue) + if e != nil { + fmt.Println("SetEntry() ERROR: e: ", e) + return + } + + fmt.Println("Testing GetEntry ==============") + + avalue, e = d.GetEntry(&tsi, akey) + if e != nil { + fmt.Println("GetEntry() ERROR: e: ", e) + return + } + + fmt.Println("ts: ", tsi, " ", akey, ": ", avalue) + + d.DeleteDB() +} diff --git a/src/translib/db/test/testdb.go b/src/translib/db/test/testdb.go new file mode 100644 index 0000000000..c28b368e3a --- /dev/null +++ b/src/translib/db/test/testdb.go @@ -0,0 +1,163 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + // "errors" + "flag" + "github.com/golang/glog" + "translib/db" + "time" + "translib/tlerr" +) + +func main() { + var avalue,rvalue db.Value + var akey,rkey db.Key + var e error + + defer glog.Flush() + + flag.Parse() + + fmt.Println("Creating the DB ==============") + d,_ := db.NewDB(db.Options { + DBNo : db.ConfigDB, + InitIndicator : "CONFIG_DB_INITIALIZED", + TableNameSeparator: "|", + KeySeparator : "|", + }) + +// fmt.Println("key: CONFIG_DB_INITIALIZED value: ", +// d.Client.Get("CONFIG_DB_INITIALIZED").String()) + + tsa := db.TableSpec { Name: "ACL_TABLE" } + tsr := db.TableSpec { Name: "ACL_RULE" } + + ca := make([]string, 1, 1) + + fmt.Println("Testing GetEntry error ==============") + ca[0] = "MyACL1_ACL_IPVNOTEXIST" + akey = db.Key { Comp: ca} + avalue, e = d.GetEntry(&tsa, akey) + fmt.Println("ts: ", tsa, " ", akey, ": ", avalue, " error: ", e) + if _, ok := e.(tlerr.TranslibRedisClientEntryNotExist) ; ok { + fmt.Println("Type is TranslibRedisClientEntryNotExist") + } + + + fmt.Println("Testing NoTransaction SetEntry ==============") + ca[0] = "MyACL1_ACL_IPV4" + akey = db.Key { Comp: ca} + avalue = db.Value { map[string]string {"ports@":"Ethernet0","type":"MIRROR" }} + + d.SetEntry(&tsa, akey, avalue) + + fmt.Println("Testing GetEntry ==============") + avalue, _ = d.GetEntry(&tsa, akey) + fmt.Println("ts: ", tsa, " ", akey, ": ", avalue) + + fmt.Println("Testing GetKeys ==============") + keys, _ := d.GetKeys(&tsa); + fmt.Println("ts: ", tsa, " keys: ", keys) + + fmt.Println("Testing NoTransaction DeleteEntry ==============") + akey = db.Key { Comp: ca} + + d.DeleteEntry(&tsa, akey) + + avalue, e = d.GetEntry(&tsa, akey) + if e == nil { + fmt.Println("!!! ts: ", tsa, " ", akey, ": ", avalue) + } + + fmt.Println("Testing 2 more ACLs ==============") + ca[0] = "MyACL2_ACL_IPV4" + avalue = db.Value { map[string]string {"ports@":"Ethernet0","type":"MIRROR" }} + d.SetEntry(&tsa, akey, avalue) + + ca[0] = "MyACL3_ACL_IPV4" + d.SetEntry(&tsa, akey, avalue) + + ta, _ := d.GetTable(&tsa) + fmt.Println("ts: ", tsa, " table: ", ta) + + tr, _ := d.GetTable(&tsr) + fmt.Println("ts: ", tsr, " table: ", tr) + + fmt.Println("Testing Transaction =================") + rkey = db.Key { Comp: []string { "MyACL2_ACL_IPV4", "RULE_1" }} + rvalue = db.Value { Field: map[string]string { + "priority" : "0", + "packet_action" : "DROP", + }, + } + +// d.StartTx([]db.WatchKeys { {Ts: &tsr, Key: &rkey} }) + d.StartTx([]db.WatchKeys {{Ts: &tsr, Key: &rkey} }, + []*db.TableSpec { &tsr, &tsa}) + + fmt.Println("Sleeping 5...") + time.Sleep(5 * time.Second) + + d.SetEntry( &tsr, rkey, rvalue) + + e = d.CommitTx() + if e != nil { + fmt.Println("Transaction Failed ======= e: ", e) + } + + + fmt.Println("Testing AbortTx =================") +// d.StartTx([]db.WatchKeys { {Ts: &tsr, Key: &rkey} }) + d.StartTx([]db.WatchKeys {}, []*db.TableSpec { &tsr, &tsa}) + d.DeleteEntry( &tsa, rkey) + d.AbortTx() + avalue, e = d.GetEntry(&tsr, rkey) + fmt.Println("ts: ", tsr, " ", akey, ": ", avalue) + + fmt.Println("Testing DeleteKeys =================") + d.DeleteKeys(&tsr, db.Key { Comp: []string {"ToBeDeletedACLs*"} }) + + fmt.Println("Testing GetTable") + tr, _ = d.GetTable(&tsr) + fmt.Println("ts: ", tsr, " table: ", tr) + + +// d.DeleteTable(&ts) + + fmt.Println("Testing Tables2TableSpecs =================") + var tables []string + tables = []string { "ACL_TABLE", "ACL_RULE" } + fmt.Println("Tables: ", tables) + fmt.Println("TableSpecs: ") + for _, tsi := range db.Tables2TableSpecs(tables) { + fmt.Println(" ", *tsi) + } + + fmt.Println("Empty TableSpecs: ") + for _, tsi := range db.Tables2TableSpecs([]string { } ) { + fmt.Println(" ", *tsi) + } + + + d.DeleteDB() +} diff --git a/src/translib/db/test/testmap.go b/src/translib/db/test/testmap.go new file mode 100644 index 0000000000..728b8842ac --- /dev/null +++ b/src/translib/db/test/testmap.go @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + // "errors" + "flag" + "github.com/golang/glog" + "translib/db" + // "time" + // "translib/tlerr" +) + + +func handler(d *db.DB, skey *db.SKey, key *db.Key, event db.SEvent) error { + fmt.Println("***handler: d: ", d, " skey: ", *skey, " key: ", *key, + " event: ", event) + return nil +} + + +func main() { + defer glog.Flush() + + flag.Parse() + + tsc := db.TableSpec { Name: "COUNTERS_PORT_NAME_MAP" } + + fmt.Println("Creating the SubscribeDB ==============") + d,e := db.NewDB(db.Options { + DBNo : db.CountersDB, + InitIndicator : "", + TableNameSeparator: ":", + KeySeparator : ":", + }) + + if e != nil { + fmt.Println("NewDB() returns error e: ", e) + } + + fmt.Println("Setting Some Maps ==============") + d.SetMap(&tsc, "Ethernet2", "oid:0x1000000000002") + d.SetMap(&tsc, "Ethernet5", "oid:0x1000000000005") + d.SetMap(&tsc, "Ethernet3", "oid:0x1000000000003") + + fmt.Println("GetMapAll ==============") + v, e := d.GetMapAll(&tsc) + if e != nil { + fmt.Println("GetMapAll() returns error e: ", e) + } + fmt.Println("v: ", v) + + fmt.Println("GetMap ==============") + r2, e := d.GetMap(&tsc, "Ethernet2") + if e != nil { + fmt.Println("GetMap() returns error e: ", e) + } + r5, e := d.GetMap(&tsc, "Ethernet5") + if e != nil { + fmt.Println("GetMap() returns error e: ", e) + } + r3, e := d.GetMap(&tsc, "Ethernet3") + if e != nil { + fmt.Println("GetMap() returns error e: ", e) + } + + fmt.Println("r2, r5, r3", r2, r5, r3) + + + fmt.Println("GetMap NotExist mapKey ==============") + rN, e := d.GetMap(&tsc, "EthernetN") + if e == nil { + fmt.Println("GetMap() NotExist mapKey returns nil !!! ", rN) + } + + vN, e := d.GetMapAll(& db.TableSpec { Name: "NOTEXITMAP" } ) + if e == nil { + fmt.Println("GetMapAll() NotExist returns nil !!! ", vN) + } + + d.DeleteMapAll(&tsc) + + d.DeleteDB() +} diff --git a/src/translib/db/test/testsubscribe.go b/src/translib/db/test/testsubscribe.go new file mode 100644 index 0000000000..298bf3443a --- /dev/null +++ b/src/translib/db/test/testsubscribe.go @@ -0,0 +1,88 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + // "errors" + "flag" + "github.com/golang/glog" + "translib/db" + "time" + // "translib/tlerr" +) + + +func handler(d *db.DB, skey *db.SKey, key *db.Key, event db.SEvent) error { + fmt.Println("***handler: d: ", d, " skey: ", *skey, " key: ", *key, + " event: ", event) + return nil +} + + +func main() { + // var avalue,rvalue db.Value + var akey db.Key + // var rkey db.Key + // var e error + + defer glog.Flush() + + flag.Parse() + + tsa := db.TableSpec { Name: "ACL_TABLE" } + // tsr := db.TableSpec { Name: "ACL_RULE" } + + ca := make([]string, 1, 1) + ca[0] = "MyACL1_ACL_IPVNOTEXIST*" + akey = db.Key { Comp: ca} + var skeys [] *db.SKey = make([]*db.SKey, 1) + skeys[0] = & (db.SKey { Ts: &tsa, Key: &akey, + SEMap: map[db.SEvent]bool { + db.SEventHSet: true, + db.SEventHDel: true, + db.SEventDel: true, + }}) + + fmt.Println("Creating the SubscribeDB ==============") + d,e := db.SubscribeDB(db.Options { + DBNo : db.ConfigDB, + InitIndicator : "CONFIG_DB_INITIALIZED", + TableNameSeparator: "|", + KeySeparator : "|", + }, skeys, handler) + + if e != nil { + fmt.Println("Subscribe() returns error e: ", e) + } + + fmt.Println("Sleeping 15 ==============") + time.Sleep(15 * time.Second) + + + fmt.Println("Testing UnsubscribeDB ==============") + + d.UnsubscribeDB() + + fmt.Println("Sleeping 5 ==============") + time.Sleep(5 * time.Second) + + +} diff --git a/src/translib/intf_app.go b/src/translib/intf_app.go new file mode 100644 index 0000000000..f318ea2740 --- /dev/null +++ b/src/translib/intf_app.go @@ -0,0 +1,435 @@ +////////////////////////////////////////////////////////////////////////// +// +// 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" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" + "reflect" + "translib/db" + "translib/ocbinds" + "translib/tlerr" +) + +type reqType int + +const ( + opCreate reqType = iota + 1 + opDelete + opUpdate +) + +type ifType int + +const ( + ETHERNET ifType = iota + VLAN + LAG +) + +type dbEntry struct { + op reqType + entry db.Value +} + +type vlanData struct { + vlanTs *db.TableSpec + vlanMemberTs *db.TableSpec + vlanTblTs *db.TableSpec + vlanMemberTblTs *db.TableSpec + + vlanMembersTableMap map[string]map[string]dbEntry +} + +type lagData struct { + lagTs *db.TableSpec + lagMemberTs *db.TableSpec + lagTblTs *db.TableSpec + lagIPTs *db.TableSpec + lagMembersTableMap map[string]map[string]dbEntry +} + +type intfData struct { + portTs *db.TableSpec + portTblTs *db.TableSpec + portOidCountrTblTs *db.TableSpec + + portOidMap dbEntry + portStatMap map[string]dbEntry + + intfIPTs *db.TableSpec + intfIPTblTs *db.TableSpec + intfCountrTblTs *db.TableSpec + + ifVlanInfoList []*ifVlan +} + +type IntfApp struct { + path *PathInfo + reqData []byte + ygotRoot *ygot.GoStruct + ygotTarget *interface{} + + respJSON interface{} + allIpKeys []db.Key + + appDB *db.DB + countersDB *db.DB + configDB *db.DB + + intfType ifType + mode intfModeCfgAlone + + intfD intfData + vlanD vlanData + lagD lagData + + ifTableMap map[string]dbEntry + ifIPTableMap map[string]map[string]dbEntry +} + +func init() { + log.Info("Init called for INTF module") + err := register("/openconfig-interfaces:interfaces", + &appInfo{appType: reflect.TypeOf(IntfApp{}), + ygotRootType: reflect.TypeOf(ocbinds.OpenconfigInterfaces_Interfaces{}), + isNative: false}) + if err != nil { + log.Fatal("Register INTF app module with App Interface failed with error=", err) + } + + err = addModel(&ModelData{Name: "openconfig-interfaces", + Org: "OpenConfig working group", + Ver: "1.0.2"}) + if err != nil { + log.Fatal("Adding model data to appinterface failed with error=", err) + } +} + +func (app *IntfApp) initializeInterface() { + app.intfD.portTs = &db.TableSpec{Name: "PORT"} + app.intfD.portTblTs = &db.TableSpec{Name: "PORT_TABLE"} + app.intfD.portOidCountrTblTs = &db.TableSpec{Name: "COUNTERS_PORT_NAME_MAP"} + + app.intfD.portStatMap = make(map[string]dbEntry) + + app.intfD.intfIPTs = &db.TableSpec{Name: "INTERFACE"} + app.intfD.intfIPTblTs = &db.TableSpec{Name: "INTF_TABLE", CompCt: 2} + app.intfD.intfCountrTblTs = &db.TableSpec{Name: "COUNTERS"} + +} + +func (app *IntfApp) initializeVlan() { + app.vlanD.vlanTs = &db.TableSpec{Name: "VLAN"} + app.vlanD.vlanMemberTs = &db.TableSpec{Name: "VLAN_MEMBER"} + app.vlanD.vlanTblTs = &db.TableSpec{Name: "VLAN_TABLE"} + app.vlanD.vlanMemberTblTs = &db.TableSpec{Name: "VLAN_MEMBER_TABLE"} + + app.vlanD.vlanMembersTableMap = make(map[string]map[string]dbEntry) +} + +func (app *IntfApp) initializeLag() { + app.lagD.lagTs = &db.TableSpec{Name: "PORTCHANNEL"} + app.lagD.lagMemberTs = &db.TableSpec{Name: "PORTCHANNEL_MEMBER"} + app.lagD.lagIPTs = &db.TableSpec{Name: "PORTCHANNEL_INTERFACE"} + app.lagD.lagTblTs = &db.TableSpec{Name: "LAG_TABLE"} + + app.lagD.lagMembersTableMap = make(map[string]map[string]dbEntry) +} + +func (app *IntfApp) initialize(data appData) { + log.Info("initialize:if:path =", data.path) + + app.path = NewPathInfo(data.path) + app.reqData = data.payload + app.ygotRoot = data.ygotRoot + app.ygotTarget = data.ygotTarget + + app.ifTableMap = make(map[string]dbEntry) + app.ifIPTableMap = make(map[string]map[string]dbEntry) + + app.initializeInterface() + app.initializeVlan() + app.initializeLag() +} + +func (app *IntfApp) getAppRootObject() *ocbinds.OpenconfigInterfaces_Interfaces { + deviceObj := (*app.ygotRoot).(*ocbinds.Device) + return deviceObj.Interfaces +} + +func (app *IntfApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCreate:intf:path =", app.path) + + err = errors.New("Not implemented") + return keys, err +} + +/* Reason why we don't have the Interface Type validation at beginning is due to, + the fact that, you could get mixture of different interfaces from GNMI or rest. + So Ideally, you need to initialize the DS for all the interface types. */ +func (app *IntfApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + pathInfo := app.path + + log.Infof("Received UPDATE for path %s; vars=%v", pathInfo.Template, pathInfo.Vars) + + intfObj := app.getAppRootObject() + + targetUriPath, err := getYangPathFromUri(app.path.Path) + log.Info("uripath:=", targetUriPath) + log.Info("err:=", err) + + if intfObj.Interface != nil && len(intfObj.Interface) > 0 { + log.Info("len:=", len(intfObj.Interface)) + for ifKey, _ := range intfObj.Interface { + log.Info("Name:=", ifKey) + err := app.getIntfTypeFromIntf(&ifKey) + if err != nil { + errStr := "Invalid Interface type:" + ifKey + ifValidErr := tlerr.InvalidArgsError{Format: errStr} + return keys, ifValidErr + } + switch app.intfType { + case ETHERNET: + keys, err = app.translateUpdatePhyIntf(d, &ifKey, opUpdate) + if err != nil { + return keys, err + } + case VLAN: + keys, err = app.translateUpdateVlanIntf(d, &ifKey, opUpdate) + if err != nil { + return keys, err + } + case LAG: + keys, err = app.translateUpdateLagIntf(d, &ifKey, opUpdate) + if err != nil { + return keys, err + } + } + } + } + return keys, err +} + +func (app *IntfApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateReplace:intf:path =", app.path) + err = errors.New("Not implemented") + return keys, err +} + +func (app *IntfApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + pathInfo := app.path + + log.Infof("Received Delete for path %s; vars=%v", pathInfo.Template, pathInfo.Vars) + + intfObj := app.getAppRootObject() + + targetUriPath, err := getYangPathFromUri(app.path.Path) + log.Info("uripath:=", targetUriPath) + log.Info("err:=", err) + + if intfObj.Interface != nil && len(intfObj.Interface) > 0 { + log.Info("len:=", len(intfObj.Interface)) + for ifKey, _ := range intfObj.Interface { + log.Info("Name:=", ifKey) + + err := app.getIntfTypeFromIntf(&ifKey) + if err != nil { + errStr := "Invalid Interface type:" + ifKey + ifValidErr := tlerr.InvalidArgsError{Format: errStr} + return keys, ifValidErr + } + switch app.intfType { + case ETHERNET: + keys, err = app.translateDeletePhyIntf(d, &ifKey) + if err != nil { + return keys, err + } + case VLAN: + keys, err = app.translateDeleteVlanIntf(d, &ifKey) + if err != nil { + return keys, err + } + case LAG: + keys, err = app.translateDeleteLagIntf(d, &ifKey) + if err != nil { + return keys, err + } + } + } + } else { + err = errors.New("Not implemented") + } + return keys, err +} + +func (app *IntfApp) translateGet(dbs [db.MaxDB]*db.DB) error { + var err error + log.Info("translateGet:intf:path =", app.path) + return err +} + +func (app *IntfApp) translateAction(dbs [db.MaxDB]*db.DB) error { + err := errors.New("Not supported") + return err +} + +func (app *IntfApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + app.appDB = dbs[db.ApplDB] + pathInfo := NewPathInfo(path) + notSupported := tlerr.NotSupportedError{Format: "Subscribe not supported", Path: path} + + if isSubtreeRequest(pathInfo.Template, "/openconfig-interfaces:interfaces") { + if pathInfo.HasSuffix("/interface{name}") || + pathInfo.HasSuffix("/config") || + pathInfo.HasSuffix("/state") { + log.Errorf("Subscribe not supported for %s!", pathInfo.Template) + return nil, nil, notSupported + } + ifKey := pathInfo.Var("name") + if len(ifKey) == 0 { + return nil, nil, errors.New("ifKey given is empty!") + } + + log.Info("Interface name = ", ifKey) + + err := app.getIntfTypeFromIntf(&ifKey) + if err != nil { + return nil, nil, err + } + + switch app.intfType { + case ETHERNET: + return app.translateSubscribePhyIntf(&ifKey, pathInfo) + case VLAN: + break + } + } + return nil, nil, notSupported +} + +func (app *IntfApp) processCreate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + log.Info("processCreate:intf:path =", app.path) + log.Info("ProcessCreate: Target Type is " + reflect.TypeOf(*app.ygotTarget).Elem().Name()) + + err = errors.New("Not implemented") + return resp, err +} + +func (app *IntfApp) processUpdate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + log.Info("processUpdate:intf:path =", app.path) + log.Info("ProcessUpdate: Target Type is " + reflect.TypeOf(*app.ygotTarget).Elem().Name()) + + switch app.intfType { + case ETHERNET: + err = app.processUpdatePhyIntf(d) + if err != nil { + return resp, err + } + case VLAN: + err = app.processUpdateVlanIntf(d) + if err != nil { + return resp, err + } + case LAG: + err = app.processUpdateLagIntf(d) + if err != nil { + return resp, err + } + } + return resp, err +} + +func (app *IntfApp) processReplace(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + log.Info("processReplace:intf:path =", app.path) + err = errors.New("Not implemented") + return resp, err +} + +func (app *IntfApp) processDelete(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + log.Info("processDelete:intf:path =", app.path) + + switch app.intfType { + case ETHERNET: + err = app.processDeletePhyIntf(d) + if err != nil { + return resp, err + } + case VLAN: + err = app.processDeleteVlanIntf(d) + if err != nil { + return resp, err + } + case LAG: + err = app.processDeleteLagIntf(d) + if err != nil { + return resp, err + } + } + return resp, err +} + +func (app *IntfApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + + var err error + var payload []byte + pathInfo := app.path + + log.Infof("Received GET for path %s; template: %s vars=%v", pathInfo.Path, pathInfo.Template, pathInfo.Vars) + app.appDB = dbs[db.ApplDB] + app.countersDB = dbs[db.CountersDB] + app.configDB = dbs[db.ConfigDB] + + targetUriPath, err := getYangPathFromUri(app.path.Path) + log.Info("URI Path = ", targetUriPath) + + if isSubtreeRequest(targetUriPath, "/openconfig-interfaces:interfaces/interface") { + return app.processGetSpecificIntf(dbs, &targetUriPath) + } + if isSubtreeRequest(targetUriPath, "/openconfig-interfaces:interfaces") { + return app.processGetAllInterfaces(dbs) + } + return GetResponse{Payload: payload}, err +} + +func (app *IntfApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) { + var resp ActionResponse + err := errors.New("Not implemented") + + return resp, err +} diff --git a/src/translib/intf_cmn.go b/src/translib/intf_cmn.go new file mode 100644 index 0000000000..06d7cc1b9d --- /dev/null +++ b/src/translib/intf_cmn.go @@ -0,0 +1,1264 @@ +////////////////////////////////////////////////////////////////////////// +// +// 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" + log "github.com/golang/glog" + "github.com/openconfig/ygot/ygot" + "net" + "strconv" + "translib/db" + "translib/ocbinds" + "translib/tlerr" + "unsafe" +) + +const ( + PORT = "PORT" + INDEX = "index" + MTU = "mtu" + ADMIN_STATUS = "admin_status" + SPEED = "speed" + DESC = "description" + OPER_STATUS = "oper_status" + NAME = "name" + ACTIVE = "active" +) + +type Table int + +const ( + IF_TABLE_MAP Table = iota + PORT_STAT_MAP +) + +func (app *IntfApp) translateUpdateIntfConfig(ifKey *string, intf *ocbinds.OpenconfigInterfaces_Interfaces_Interface, curr *db.Value) { + if intf.Config != nil { + if intf.Config.Description != nil { + curr.Field["description"] = *intf.Config.Description + } else if intf.Config.Mtu != nil { + curr.Field["mtu"] = strconv.Itoa(int(*intf.Config.Mtu)) + } else if intf.Config.Enabled != nil { + if *intf.Config.Enabled == true { + curr.Field["admin_status"] = "up" + } else { + curr.Field["admin_status"] = "down" + } + } + } + log.Info("Writing to db for ", *ifKey) + app.ifTableMap[*ifKey] = dbEntry{op: opUpdate, entry: *curr} +} + +/* Handling IP address updates for given interface */ +func (app *IntfApp) translateUpdateIntfSubInterfaces(d *db.DB, ifKey *string, intf *ocbinds.OpenconfigInterfaces_Interfaces_Interface) error { + var err error + if intf.Subinterfaces == nil { + return err + } + subIf := intf.Subinterfaces.Subinterface[0] + if subIf != nil { + if subIf.Ipv4 != nil && subIf.Ipv4.Addresses != nil { + for ip, _ := range subIf.Ipv4.Addresses.Address { + addr := subIf.Ipv4.Addresses.Address[ip] + if addr.Config != nil { + log.Info("Ip:=", *addr.Config.Ip) + log.Info("prefix:=", *addr.Config.PrefixLength) + if !validIPv4(*addr.Config.Ip) { + errStr := "Invalid IPv4 address " + *addr.Config.Ip + err = tlerr.InvalidArgsError{Format: errStr} + return err + } + err = app.translateIpv4(d, *ifKey, *addr.Config.Ip, int(*addr.Config.PrefixLength)) + if err != nil { + return err + } + } + } + } + if subIf.Ipv6 != nil && subIf.Ipv6.Addresses != nil { + for ip, _ := range subIf.Ipv6.Addresses.Address { + addr := subIf.Ipv6.Addresses.Address[ip] + if addr.Config != nil { + log.Info("Ip:=", *addr.Config.Ip) + log.Info("prefix:=", *addr.Config.PrefixLength) + if !validIPv6(*addr.Config.Ip) { + errStr := "Invalid IPv6 address " + *addr.Config.Ip + err = tlerr.InvalidArgsError{Format: errStr} + return err + } + err = app.translateIpv4(d, *ifKey, *addr.Config.Ip, int(*addr.Config.PrefixLength)) + if err != nil { + return err + } + } + } + } + } else { + err = errors.New("Only subinterface index 0 is supported") + return err + } + return err +} + +/* Handling IP address configuration for given Interface */ +func (app *IntfApp) processUpdateIntfSubInterfaces(d *db.DB) error { + var err error + /* Updating the table */ + for ifName, ipEntries := range app.ifIPTableMap { + ts := app.intfD.intfIPTs + if app.intfType == LAG { + ts = app.lagD.lagIPTs + } + m := make(map[string]string) + m["NULL"] = "NULL" + ifEntry, err := d.GetEntry(ts, db.Key{Comp: []string{ifName}}) + if err != nil || !ifEntry.IsPopulated() { + err = d.CreateEntry(ts, db.Key{Comp: []string{ifName}}, db.Value{Field: m}) + if err != nil { + log.Info("Failed to create Interface entry with Interface name") + return err + } + log.Infof("Created Interface entry with Interface name : %s alone!", ifName) + } + for ip, ipEntry := range ipEntries { + if ipEntry.op == opCreate { + log.Info("Creating entry for ", ifName, ":", ip) + err = d.CreateEntry(ts, db.Key{Comp: []string{ifName, ip}}, ipEntry.entry) + if err != nil { + errStr := "Creating entry for " + ifName + ":" + ip + " failed" + return errors.New(errStr) + } + } else if ipEntry.op == opDelete { + log.Info("Deleting entry for ", ifName, ":", ip) + err = d.DeleteEntry(ts, db.Key{Comp: []string{ifName, ip}}) + if err != nil { + errStr := "Deleting entry for " + ifName + ":" + ip + " failed" + return errors.New(errStr) + } + } + } + } + return err +} + +func (app *IntfApp) translateDeleteIntfSubInterfaces(d *db.DB, intf *ocbinds.OpenconfigInterfaces_Interfaces_Interface, ifName *string) error { + var err error + if intf.Subinterfaces == nil { + return err + } + err = app.getIntfTypeFromIntf(ifName) + if err != nil { + return err + } + /* Find the type of Interface*/ + ts := app.intfD.intfIPTs + if app.intfType == LAG { + ts = app.lagD.lagIPTs + } + subIf := intf.Subinterfaces.Subinterface[0] + if subIf != nil { + if subIf.Ipv4 != nil && subIf.Ipv4.Addresses != nil { + for ip, _ := range subIf.Ipv4.Addresses.Address { + addr := subIf.Ipv4.Addresses.Address[ip] + if addr != nil { + ipAddr := addr.Ip + log.Info("IPv4 address = ", *ipAddr) + if !validIPv4(*ipAddr) { + errStr := "Invalid IPv4 address " + *ipAddr + ipValidErr := tlerr.InvalidArgsError{Format: errStr} + return ipValidErr + } + err = app.validateIp(d, *ifName, *ipAddr, ts) + if err != nil { + errStr := "Invalid IPv4 address " + *ipAddr + ipValidErr := tlerr.InvalidArgsError{Format: errStr} + return ipValidErr + } + } + } + } + if subIf.Ipv6 != nil && subIf.Ipv6.Addresses != nil { + for ip, _ := range subIf.Ipv6.Addresses.Address { + addr := subIf.Ipv6.Addresses.Address[ip] + if addr != nil { + ipAddr := addr.Ip + log.Info("IPv6 address = ", *ipAddr) + if !validIPv6(*ipAddr) { + errStr := "Invalid IPv6 address " + *ipAddr + ipValidErr := tlerr.InvalidArgsError{Format: errStr} + return ipValidErr + } + err = app.validateIp(d, *ifName, *ipAddr, ts) + if err != nil { + errStr := "Invalid IPv6 address:" + *ipAddr + ipValidErr := tlerr.InvalidArgsError{Format: errStr} + return ipValidErr + } + } + } + } + } else { + err = errors.New("Only subinterface index 0 is supported") + return err + } + return err +} + +func (app *IntfApp) getSpecificIfStateAttr(targetUriPath *string, ifKey *string, oc_val *ocbinds.OpenconfigInterfaces_Interfaces_Interface_State) (bool, error) { + switch *targetUriPath { + + case "/openconfig-interfaces:interfaces/interface/state/oper-status": + val, e := app.getIntfAttr(ifKey, OPER_STATUS, IF_TABLE_MAP) + if len(val) > 0 { + switch val { + case "up": + oc_val.OperStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_OperStatus_UP + case "down": + oc_val.OperStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_OperStatus_DOWN + default: + oc_val.OperStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_OperStatus_UNSET + } + return true, nil + } else { + return true, e + } + case "/openconfig-interfaces:interfaces/interface/state/admin-status": + val, e := app.getIntfAttr(ifKey, ADMIN_STATUS, IF_TABLE_MAP) + if len(val) > 0 { + switch val { + case "up": + oc_val.AdminStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_AdminStatus_UP + case "down": + oc_val.AdminStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_AdminStatus_DOWN + default: + oc_val.AdminStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_AdminStatus_UNSET + } + return true, nil + } else { + return true, e + } + case "/openconfig-interfaces:interfaces/interface/state/mtu": + val, e := app.getIntfAttr(ifKey, MTU, IF_TABLE_MAP) + if len(val) > 0 { + v, e := strconv.ParseUint(val, 10, 16) + if e == nil { + oc_val.Mtu = (*uint16)(unsafe.Pointer(&v)) + return true, nil + } + } + return true, e + case "/openconfig-interfaces:interfaces/interface/state/ifindex": + val, e := app.getIntfAttr(ifKey, INDEX, IF_TABLE_MAP) + if len(val) > 0 { + v, e := strconv.ParseUint(val, 10, 32) + if e == nil { + oc_val.Ifindex = (*uint32)(unsafe.Pointer(&v)) + return true, nil + } + } + return true, e + case "/openconfig-interfaces:interfaces/interface/state/description": + val, e := app.getIntfAttr(ifKey, DESC, IF_TABLE_MAP) + if e == nil { + oc_val.Description = &val + return true, nil + } + return true, e + default: + log.Infof(*targetUriPath + " - Not an interface state attribute") + } + return false, nil +} + +func (app *IntfApp) getSpecificIfVlanAttr(targetUriPath *string, ifKey *string, oc_val *ocbinds.OpenconfigInterfaces_Interfaces_Interface_Ethernet_SwitchedVlan_State) (bool, error) { + switch *targetUriPath { + case "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/state/access-vlan": + _, accessVlanName, e := app.getIntfVlanAttr(ifKey, ACCESS) + if e != nil { + return true, e + } + if accessVlanName == nil { + return true, nil + } + vlanName := *accessVlanName + vlanIdStr := vlanName[len("Vlan"):len(vlanName)] + vlanId, err := strconv.Atoi(vlanIdStr) + if err != nil { + errStr := "Conversion of string to int failed for " + vlanIdStr + return true, errors.New(errStr) + } + vlanIdCast := uint16(vlanId) + + oc_val.AccessVlan = &vlanIdCast + return true, nil + case "/openconfig-interfaces:interfaces/interface/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/state/trunk-vlans": + trunkVlans, _, e := app.getIntfVlanAttr(ifKey, TRUNK) + if e != nil { + return true, e + } + for _, vlanName := range trunkVlans { + vlanIdStr := vlanName[len("Vlan"):len(vlanName)] + vlanId, err := strconv.Atoi(vlanIdStr) + if err != nil { + errStr := "Conversion of string to int failed for " + vlanIdStr + return true, errors.New(errStr) + } + vlanIdCast := uint16(vlanId) + + trunkVlan, _ := oc_val.To_OpenconfigInterfaces_Interfaces_Interface_Ethernet_SwitchedVlan_State_TrunkVlans_Union(vlanIdCast) + oc_val.TrunkVlans = append(oc_val.TrunkVlans, trunkVlan) + } + return true, nil + } + return false, nil +} + +func (app *IntfApp) getSpecificIfLagAttr(d *db.DB, targetUriPath *string, ifKey *string, oc_val *ocbinds.OpenconfigInterfaces_Interfaces_Interface_Aggregation_State) (bool, error) { + switch *targetUriPath { + case "/openconfig-interfaces:interfaces/interface/openconfig-if-aggregate:aggregation/state/min-links": + curr, err := d.GetEntry(app.lagD.lagTs, db.Key{Comp: []string{*ifKey}}) + if err != nil { + errStr := "Failed to Get PortChannel details" + return true, errors.New(errStr) + } + if val, ok := curr.Field["min_links"]; ok { + log.Info("curr.Field['min_links']", val) + min_links, err := strconv.Atoi(curr.Field["min_links"]) + if err != nil { + errStr := "Conversion of string to int failed" + return true, errors.New(errStr) + } + links := uint16(min_links) + oc_val.MinLinks = &links + } else { + log.Info("Minlinks set to 0 (dafault value)") + links := uint16(0) + oc_val.MinLinks = &links + } + return true, nil + case "/openconfig-interfaces:interfaces/interface/openconfig-if-aggregate:aggregation/state/member": + lagKeys, err := d.GetKeys(app.lagD.lagMemberTs) + if err != nil { + log.Info("No entries in PORTCHANNEL_MEMBER TABLE") + return true, err + } + var flag bool = false + for i, _ := range lagKeys { + if *ifKey == lagKeys[i].Get(0) { + log.Info("Found lagKey") + flag = true + ethName := lagKeys[i].Get(1) + oc_val.Member = append(oc_val.Member, ethName) + } + } + if flag == false { + log.Info("Given PortChannel has no members") + errStr := "Given PortChannel has no members" + return true, errors.New(errStr) + } + return true, nil + case "/openconfig-interfaces:interfaces/interface/openconfig-if-aggregate:aggregation/state/dell-intf-augments:fallback": + curr, err := d.GetEntry(app.lagD.lagTs, db.Key{Comp: []string{*ifKey}}) + if err != nil { + errStr := "Failed to Get PortChannel details" + return true, errors.New(errStr) + } + if val, ok := curr.Field["fallback"]; ok { + log.Info("curr.Field['fallback']", val) + fallbackVal, err := strconv.ParseBool(val) + if err != nil { + errStr := "Conversion of string to bool failed" + return true, errors.New(errStr) + } + oc_val.Fallback = &fallbackVal + } else { + log.Info("Fallback set to False, default value") + fallbackVal := false + oc_val.Fallback = &fallbackVal + } + return true, nil + + default: + log.Infof(*targetUriPath + " - Not an supported Get attribute") + } + return false, nil +} + +func (app *IntfApp) getSpecificCounterAttr(targetUriPath *string, ifKey *string, counter_val *ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_Counters) (bool, error) { + + var e error + + switch *targetUriPath { + case "/openconfig-interfaces:interfaces/interface/state/counters/in-octets": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_OCTETS", &counter_val.InOctets) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/in-unicast-pkts": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_UCAST_PKTS", &counter_val.InUnicastPkts) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/in-broadcast-pkts": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_BROADCAST_PKTS", &counter_val.InBroadcastPkts) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/in-multicast-pkts": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_MULTICAST_PKTS", &counter_val.InMulticastPkts) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/in-errors": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_ERRORS", &counter_val.InErrors) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/in-discards": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_DISCARDS", &counter_val.InDiscards) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/in-pkts": + var inNonUCastPkt, inUCastPkt *uint64 + var in_pkts uint64 + + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS", &inNonUCastPkt) + if e == nil { + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_IN_UCAST_PKTS", &inUCastPkt) + if e != nil { + return true, e + } + in_pkts = *inUCastPkt + *inNonUCastPkt + counter_val.InPkts = &in_pkts + return true, e + } else { + return true, e + } + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-octets": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_OCTETS", &counter_val.OutOctets) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-unicast-pkts": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_UCAST_PKTS", &counter_val.OutUnicastPkts) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-broadcast-pkts": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS", &counter_val.OutBroadcastPkts) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-multicast-pkts": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS", &counter_val.OutMulticastPkts) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-errors": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_ERRORS", &counter_val.OutErrors) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-discards": + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_DISCARDS", &counter_val.OutDiscards) + return true, e + + case "/openconfig-interfaces:interfaces/interface/state/counters/out-pkts": + var outNonUCastPkt, outUCastPkt *uint64 + var out_pkts uint64 + + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS", &outNonUCastPkt) + if e == nil { + e = app.getCounters(ifKey, "SAI_PORT_STAT_IF_OUT_UCAST_PKTS", &outUCastPkt) + if e != nil { + return true, e + } + out_pkts = *outUCastPkt + *outNonUCastPkt + counter_val.OutPkts = &out_pkts + return true, e + } else { + return true, e + } + + default: + log.Infof(*targetUriPath + " - Not an interface state counter attribute") + } + return false, nil +} + +func (app *IntfApp) getCounters(ifKey *string, attr string, counter_val **uint64) error { + val, e := app.getIntfAttr(ifKey, attr, PORT_STAT_MAP) + if len(val) > 0 { + v, e := strconv.ParseUint(val, 10, 64) + if e == nil { + *counter_val = &v + return nil + } + } + return e +} + +func (app *IntfApp) getIntfAttr(ifName *string, attr string, table Table) (string, error) { + + var ok bool = false + var entry dbEntry + + if table == IF_TABLE_MAP { + entry, ok = app.ifTableMap[*ifName] + } else if table == PORT_STAT_MAP { + entry, ok = app.intfD.portStatMap[*ifName] + } else { + return "", errors.New("Unsupported table") + } + + if ok { + ifData := entry.entry + + if val, ok := ifData.Field[attr]; ok { + return val, nil + } + } + return "", errors.New("Attr " + attr + "doesn't exist in IF table Map!") +} + +func (app *IntfApp) getIntfVlanAttr(ifName *string, ifMode intfModeType) ([]string, *string, error) { + + vlanEntries, ok := app.vlanD.vlanMembersTableMap[*ifName] + if !ok { + errStr := "Cannot find info for Interface: " + *ifName + " from VLAN_MEMBERS_TABLE!" + return nil, nil, errors.New(errStr) + } + switch ifMode { + case ACCESS: + for vlanKey, tagEntry := range vlanEntries { + tagMode, ok := tagEntry.entry.Field["tagging_mode"] + if ok { + if tagMode == "untagged" { + log.Info("Untagged VLAN found!") + return nil, &vlanKey, nil + } + } + } + case TRUNK: + var trunkVlans []string + for vlanKey, tagEntry := range vlanEntries { + tagMode, ok := tagEntry.entry.Field["tagging_mode"] + if ok { + if tagMode == "tagged" { + trunkVlans = append(trunkVlans, vlanKey) + } + } + } + return trunkVlans, nil, nil + } + return nil, nil, nil +} + +func (app *IntfApp) processGetSpecificAttr(targetUriPath *string, ifKey *string) (bool, *GetResponse, error) { + var err error + var payload []byte + + /*Check if the request is for a specific attribute in Interfaces state container*/ + ocStateVal := &ocbinds.OpenconfigInterfaces_Interfaces_Interface_State{} + ok, e := app.getSpecificIfStateAttr(targetUriPath, ifKey, ocStateVal) + if ok { + if e != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), e + } + payload, err = dumpIetfJson(ocStateVal, false) + if err != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), err + } + return ok, &(GetResponse{Payload: payload}), err + + } + + /*Check if the request is for a specific attribute in Interfaces state COUNTERS container*/ + counter_val := &ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_Counters{} + ok, e = app.getSpecificCounterAttr(targetUriPath, ifKey, counter_val) + if ok { + if e != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), e + } + + payload, err = dumpIetfJson(counter_val, false) + if err != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), err + } + return ok, &(GetResponse{Payload: payload}), err + } + + /*Check if the request is for a specific attribute in Interfaces Ethernet container*/ + ocEthernetVlanStateVal := &ocbinds.OpenconfigInterfaces_Interfaces_Interface_Ethernet_SwitchedVlan_State{} + ok, e = app.getSpecificIfVlanAttr(targetUriPath, ifKey, ocEthernetVlanStateVal) + if ok { + if e != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), e + } + payload, err = dumpIetfJson(ocEthernetVlanStateVal, false) + if err != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), err + } + return ok, &(GetResponse{Payload: payload}), err + } + /*Check if the request is for a specific attribute in Interfaces Aggregation container*/ + ocEthernetLagStateVal := &ocbinds.OpenconfigInterfaces_Interfaces_Interface_Aggregation_State{} + ok, e = app.getSpecificIfLagAttr(app.configDB, targetUriPath, ifKey, ocEthernetLagStateVal) + if ok { + if e != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), e + } + payload, err = dumpIetfJson(ocEthernetLagStateVal, false) + if err != nil { + return ok, &(GetResponse{Payload: payload, ErrSrc: AppErr}), err + } + return ok, &(GetResponse{Payload: payload}), err + } + return ok, &(GetResponse{Payload: payload}), err +} + +func (app *IntfApp) getPortOidMapForCounters(dbCl *db.DB) error { + var err error + ifCountInfo, err := dbCl.GetMapAll(app.intfD.portOidCountrTblTs) + if err != nil { + log.Error("Port-OID (Counters) get for all the interfaces failed!") + return err + } + if ifCountInfo.IsPopulated() { + app.intfD.portOidMap.entry = ifCountInfo + } else { + return errors.New("Get for OID info from all the interfaces from Counters DB failed!") + } + return err +} + +func (app *IntfApp) convertDBIntfCounterInfoToInternal(dbCl *db.DB, ifName *string) error { + var err error + + if len(*ifName) > 0 { + oid := app.intfD.portOidMap.entry.Field[*ifName] + log.Infof("OID : %s received for Interface : %s", oid, *ifName) + + /* Get the statistics for the port */ + var ifStatKey db.Key + ifStatKey.Comp = []string{oid} + + ifStatInfo, err := dbCl.GetEntry(app.intfD.intfCountrTblTs, ifStatKey) + if err != nil { + log.Infof("Fetching port-stat for port : %s failed!", *ifName) + return err + } + app.intfD.portStatMap[*ifName] = dbEntry{entry: ifStatInfo} + } else { + for ifName, _ := range app.ifTableMap { + app.convertDBIntfCounterInfoToInternal(dbCl, &ifName) + } + } + return err +} + +func (app *IntfApp) convertDBIfVlanListInfoToInternal(dbCl *db.DB, ts *db.TableSpec, ifName *string) error { + var err error + + vlanMemberTable, err := dbCl.GetTable(ts) + if err != nil { + return err + } + vlanMemberKeys, err := vlanMemberTable.GetKeys() + if err != nil { + return err + } + log.Infof("Found %d vlan-member-table keys", len(vlanMemberKeys)) + + for _, vlanMember := range vlanMemberKeys { + if len(vlanMember.Comp) < 2 { + continue + } + vlanId := vlanMember.Get(0) + ifName := vlanMember.Get(1) + log.Infof("Received Vlan: %s for Interface: %s", vlanId, ifName) + + memberPortEntry, err := dbCl.GetEntry(ts, vlanMember) + if err != nil { + return err + } + if !memberPortEntry.IsPopulated() { + errStr := "Tagging Info not present for Vlan: " + vlanId + " Interface: " + ifName + " from VLAN_MEMBER_TABLE" + return errors.New(errStr) + } + + /* vlanMembersTableMap is used as DS for ifName to list of VLANs */ + if app.vlanD.vlanMembersTableMap[ifName] == nil { + app.vlanD.vlanMembersTableMap[ifName] = make(map[string]dbEntry) + app.vlanD.vlanMembersTableMap[ifName][vlanId] = dbEntry{entry: memberPortEntry} + } else { + app.vlanD.vlanMembersTableMap[ifName][vlanId] = dbEntry{entry: memberPortEntry} + } + } + log.Infof("Updated the vlan-member-table ds for Interface: %s", *ifName) + return err +} + +func (app *IntfApp) convertDBIntfInfoToInternal(dbCl *db.DB, ts *db.TableSpec, ifName *string, ifKey db.Key) error { + + var err error + /* Fetching DB data for a specific Interface */ + if len(*ifName) > 0 { + log.Info("Updating Interface info from APP-DB to Internal DS for Interface name : ", *ifName) + ifInfo, err := dbCl.GetEntry(ts, ifKey) + if err != nil { + log.Errorf("Error found on fetching Interface info from App DB for If Name : %s", *ifName) + errStr := "Invalid Interface:" + *ifName + err = tlerr.InvalidArgsError{Format: errStr} + return err + } + if ifInfo.IsPopulated() { + log.Info("Interface Info populated for ifName : ", *ifName) + app.ifTableMap[*ifName] = dbEntry{entry: ifInfo} + } else { + return errors.New("Populating Interface info for " + *ifName + "failed") + } + } else { + log.Info("App-DB get for all the interfaces") + tbl, err := dbCl.GetTable(ts) + if err != nil { + log.Error("App-DB get for list of interfaces failed!") + return err + } + keys, _ := tbl.GetKeys() + for _, key := range keys { + ifName := key.Get(0) + app.convertDBIntfInfoToInternal(dbCl, ts, &(ifName), db.Key{Comp: []string{key.Get(0)}}) + } + } + return err +} + +func (app *IntfApp) convertDBIntfIPInfoToInternal(dbCl *db.DB, ts *db.TableSpec, ifName *string) error { + + var err error + log.Info("Updating Interface IP Info from APP-DB to Internal DS for Interface Name : ", *ifName) + app.allIpKeys, _ = app.doGetAllIpKeys(dbCl, ts) + + for _, key := range app.allIpKeys { + if len(key.Comp) <= 1 { + continue + } + ipInfo, err := dbCl.GetEntry(ts, key) + if err != nil { + log.Errorf("Error found on fetching Interface IP info from App DB for Interface Name : %s", *ifName) + return err + } + if len(app.ifIPTableMap[key.Get(0)]) == 0 { + app.ifIPTableMap[key.Get(0)] = make(map[string]dbEntry) + app.ifIPTableMap[key.Get(0)][key.Get(1)] = dbEntry{entry: ipInfo} + } else { + app.ifIPTableMap[key.Get(0)][key.Get(1)] = dbEntry{entry: ipInfo} + } + } + return err +} + +func (app *IntfApp) processGetConvertDBPhyIfInfoToDS(ifName *string) error { + var err error + + err = app.convertDBIntfInfoToInternal(app.appDB, app.intfD.portTblTs, ifName, db.Key{Comp: []string{*ifName}}) + if err != nil { + return err + } + + err = app.convertDBIfVlanListInfoToInternal(app.appDB, app.vlanD.vlanMemberTblTs, ifName) + if err != nil { + return err + } + + err = app.getPortOidMapForCounters(app.countersDB) + if err != nil { + return err + } + err = app.convertDBIntfCounterInfoToInternal(app.countersDB, ifName) + if err != nil { + return err + } + + err = app.convertDBIntfIPInfoToInternal(app.appDB, app.intfD.intfIPTblTs, ifName) + if err != nil { + return err + } + return err +} + +func (app *IntfApp) processGetConvertDBVlanIfInfoToDS(vlanName *string) error { + var err error + + err = app.convertDBIntfInfoToInternal(app.appDB, app.vlanD.vlanTblTs, vlanName, db.Key{Comp: []string{*vlanName}}) + if err != nil { + return err + } + return err +} + +func (app *IntfApp) processGetConvertDBLagIfInfoToDS(lagName *string) error { + var err error + + err = app.convertDBIntfInfoToInternal(app.appDB, app.lagD.lagTblTs, lagName, db.Key{Comp: []string{*lagName}}) + if err != nil { + return err + } + return err +} + +func (app *IntfApp) processGetConvertDBIfInfoToDS(ifName *string) error { + var err error + + err = app.processGetConvertDBPhyIfInfoToDS(ifName) + if err != nil { + return err + } + + err = app.processGetConvertDBVlanIfInfoToDS(ifName) + if err != nil { + return err + } + return err +} + +func (app *IntfApp) convertInternalToOCIntfAttrInfo(ifName *string, ifInfo *ocbinds.OpenconfigInterfaces_Interfaces_Interface) { + + /* Handling the Interface attributes */ + if ifInfo.Config == nil || ifInfo.State == nil { + return + } + if entry, ok := app.ifTableMap[*ifName]; ok { + ifData := entry.entry + + name := *ifName + ifInfo.Config.Name = &name + ifInfo.State.Name = &name + + for ifAttr := range ifData.Field { + switch ifAttr { + case ADMIN_STATUS: + adminStatus := ifData.Get(ifAttr) + ifInfo.State.AdminStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_AdminStatus_DOWN + if adminStatus == "up" { + ifInfo.State.AdminStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_AdminStatus_UP + } + case OPER_STATUS: + operStatus := ifData.Get(ifAttr) + ifInfo.State.OperStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_OperStatus_DOWN + if operStatus == "up" { + ifInfo.State.OperStatus = ocbinds.OpenconfigInterfaces_Interfaces_Interface_State_OperStatus_UP + } + case DESC: + descVal := ifData.Get(ifAttr) + descr := new(string) + *descr = descVal + ifInfo.Config.Description = descr + ifInfo.State.Description = descr + case MTU: + mtuStr := ifData.Get(ifAttr) + mtuVal, err := strconv.Atoi(mtuStr) + mtu := new(uint16) + *mtu = uint16(mtuVal) + if err == nil { + ifInfo.Config.Mtu = mtu + ifInfo.State.Mtu = mtu + } + case SPEED: + speed := ifData.Get(ifAttr) + var speedEnum ocbinds.E_OpenconfigIfEthernet_ETHERNET_SPEED + + switch speed { + case "2500": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_2500MB + case "1000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_1GB + case "5000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_5GB + case "10000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_10GB + case "25000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_25GB + case "40000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_40GB + case "50000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_50GB + case "100000": + speedEnum = ocbinds.OpenconfigIfEthernet_ETHERNET_SPEED_SPEED_100GB + default: + log.Infof("Not supported speed: %s!", speed) + } + if ifInfo.Ethernet != nil { + if ifInfo.Ethernet.Config != nil { + ifInfo.Ethernet.Config.PortSpeed = speedEnum + } + if ifInfo.Ethernet.State != nil { + ifInfo.Ethernet.State.PortSpeed = speedEnum + } + } + case INDEX: + ifIdxStr := ifData.Get(ifAttr) + ifIdxNum, err := strconv.Atoi(ifIdxStr) + if err == nil { + ifIdx := new(uint32) + *ifIdx = uint32(ifIdxNum) + ifInfo.State.Ifindex = ifIdx + } + case ACTIVE: + case NAME: + default: + log.Info("Not a valid attribute!") + } + } + } +} + +func (app *IntfApp) convertInternalToOCIntfVlanListInfo(ifName *string, ifInfo *ocbinds.OpenconfigInterfaces_Interfaces_Interface) error { + var err error + if ifInfo.Ethernet == nil || ifInfo.Ethernet.SwitchedVlan == nil || ifInfo.Ethernet.SwitchedVlan.State == nil { + return nil + } + taggedMemberPresent := false + + if len(*ifName) < 0 { + return nil + } + vlanMap, ok := app.vlanD.vlanMembersTableMap[*ifName] + if ok { + for vlanName, tagEntry := range vlanMap { + vlanIdStr := vlanName[len("Vlan"):len(vlanName)] + vlanId, err := strconv.Atoi(vlanIdStr) + vlanIdCast := uint16(vlanId) + + tagMode := tagEntry.entry.Field["tagging_mode"] + if tagMode == "untagged" { + if err != nil { + errStr := "Translation of Vlan-name: " + vlanName + " to vlan-id failed!" + return errors.New(errStr) + } + ifInfo.Ethernet.SwitchedVlan.State.AccessVlan = &(vlanIdCast) + log.Infof("Adding access-vlan: %d succesful!", vlanIdCast) + } else { + taggedMemberPresent = true + trunkVlan, _ := ifInfo.Ethernet.SwitchedVlan.State.To_OpenconfigInterfaces_Interfaces_Interface_Ethernet_SwitchedVlan_State_TrunkVlans_Union(vlanIdCast) + ifInfo.Ethernet.SwitchedVlan.State.TrunkVlans = append(ifInfo.Ethernet.SwitchedVlan.State.TrunkVlans, trunkVlan) + log.Infof("Adding trunk-vlan: %d succesful!", trunkVlan) + } + + } + if taggedMemberPresent { + ifInfo.Ethernet.SwitchedVlan.State.InterfaceMode = ocbinds.OpenconfigVlan_VlanModeType_TRUNK + } else { + ifInfo.Ethernet.SwitchedVlan.State.InterfaceMode = ocbinds.OpenconfigVlan_VlanModeType_ACCESS + } + } + return err +} + +func (app *IntfApp) convertInternalToOCIntfIPAttrInfo(ifName *string, ifInfo *ocbinds.OpenconfigInterfaces_Interfaces_Interface) { + + /* Handling the Interface IP attributes */ + subIntf, err := ifInfo.Subinterfaces.NewSubinterface(0) + if err != nil { + log.Error("Creation of subinterface subtree failed!") + return + } + if subIntf == nil { + return + } + ygot.BuildEmptyTree(subIntf) + if ipMap, ok := app.ifIPTableMap[*ifName]; ok { + for ipKey, _ := range ipMap { + log.Info("IP address = ", ipKey) + ipB, ipNetB, _ := net.ParseCIDR(ipKey) + + v4Flag := false + v6Flag := false + + var v4Address *ocbinds.OpenconfigInterfaces_Interfaces_Interface_Subinterfaces_Subinterface_Ipv4_Addresses_Address + var v6Address *ocbinds.OpenconfigInterfaces_Interfaces_Interface_Subinterfaces_Subinterface_Ipv6_Addresses_Address + + if validIPv4(ipB.String()) { + v4Address, err = subIntf.Ipv4.Addresses.NewAddress(ipB.String()) + v4Flag = true + } else if validIPv6(ipB.String()) { + v6Address, err = subIntf.Ipv6.Addresses.NewAddress(ipB.String()) + v6Flag = true + } else { + log.Error("Invalid IP address " + ipB.String()) + continue + } + + if err != nil { + log.Error("Creation of address subtree failed!") + return + } + if v4Flag { + ygot.BuildEmptyTree(v4Address) + + ipStr := new(string) + *ipStr = ipB.String() + v4Address.Ip = ipStr + v4Address.Config.Ip = ipStr + v4Address.State.Ip = ipStr + + ipNetBNum, _ := ipNetB.Mask.Size() + prfxLen := new(uint8) + *prfxLen = uint8(ipNetBNum) + v4Address.Config.PrefixLength = prfxLen + v4Address.State.PrefixLength = prfxLen + } + if v6Flag { + ygot.BuildEmptyTree(v6Address) + + ipStr := new(string) + *ipStr = ipB.String() + v6Address.Ip = ipStr + v6Address.Config.Ip = ipStr + v6Address.State.Ip = ipStr + + ipNetBNum, _ := ipNetB.Mask.Size() + prfxLen := new(uint8) + *prfxLen = uint8(ipNetBNum) + v6Address.Config.PrefixLength = prfxLen + v6Address.State.PrefixLength = prfxLen + } + } + } +} + +func (app *IntfApp) convertInternalToOCPortStatInfo(ifName *string, ifInfo *ocbinds.OpenconfigInterfaces_Interfaces_Interface) { + if len(app.intfD.portStatMap) == 0 { + log.Info("Port stat info not present for interface :", *ifName) + return + } + if ifInfo.State == nil || ifInfo.State.Counters == nil { + return + } + if portStatInfo, ok := app.intfD.portStatMap[*ifName]; ok { + log.Info("Entered Counters filling") + + inOctet := new(uint64) + inOctetVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_OCTETS"]) + *inOctet = uint64(inOctetVal) + ifInfo.State.Counters.InOctets = inOctet + + inUCastPkt := new(uint64) + inUCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_UCAST_PKTS"]) + *inUCastPkt = uint64(inUCastPktVal) + ifInfo.State.Counters.InUnicastPkts = inUCastPkt + + inNonUCastPkt := new(uint64) + inNonUCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS"]) + *inNonUCastPkt = uint64(inNonUCastPktVal) + + inPkt := new(uint64) + *inPkt = *inUCastPkt + *inNonUCastPkt + ifInfo.State.Counters.InPkts = inPkt + + inBCastPkt := new(uint64) + inBCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_BROADCAST_PKTS"]) + *inBCastPkt = uint64(inBCastPktVal) + ifInfo.State.Counters.InBroadcastPkts = inBCastPkt + + inMCastPkt := new(uint64) + inMCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_MULTICAST_PKTS"]) + *inMCastPkt = uint64(inMCastPktVal) + ifInfo.State.Counters.InMulticastPkts = inMCastPkt + + inErrPkt := new(uint64) + inErrPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_ERRORS"]) + *inErrPkt = uint64(inErrPktVal) + ifInfo.State.Counters.InErrors = inErrPkt + + inDiscPkt := new(uint64) + inDiscPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_IN_DISCARDS"]) + *inDiscPkt = uint64(inDiscPktVal) + ifInfo.State.Counters.InDiscards = inDiscPkt + + outOctet := new(uint64) + outOctetVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_OCTETS"]) + *outOctet = uint64(outOctetVal) + ifInfo.State.Counters.OutOctets = outOctet + + outUCastPkt := new(uint64) + outUCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_UCAST_PKTS"]) + *outUCastPkt = uint64(outUCastPktVal) + ifInfo.State.Counters.OutUnicastPkts = outUCastPkt + + outNonUCastPkt := new(uint64) + outNonUCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS"]) + *outNonUCastPkt = uint64(outNonUCastPktVal) + + outPkt := new(uint64) + *outPkt = *outUCastPkt + *outNonUCastPkt + ifInfo.State.Counters.OutPkts = outPkt + + outBCastPkt := new(uint64) + outBCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS"]) + *outBCastPkt = uint64(outBCastPktVal) + ifInfo.State.Counters.OutBroadcastPkts = outBCastPkt + + outMCastPkt := new(uint64) + outMCastPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS"]) + *outMCastPkt = uint64(outMCastPktVal) + ifInfo.State.Counters.OutMulticastPkts = outMCastPkt + + outErrPkt := new(uint64) + outErrPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_ERRORS"]) + *outErrPkt = uint64(outErrPktVal) + ifInfo.State.Counters.OutErrors = outErrPkt + + outDiscPkt := new(uint64) + outDiscPktVal, _ := strconv.Atoi(portStatInfo.entry.Field["SAI_PORT_STAT_IF_OUT_DISCARDS"]) + *outDiscPkt = uint64(outDiscPktVal) + ifInfo.State.Counters.OutDiscards = outDiscPkt + } +} + +func (app *IntfApp) convertInternalToOCIntfInfo(ifName *string, ifInfo *ocbinds.OpenconfigInterfaces_Interfaces_Interface) { + app.convertInternalToOCIntfAttrInfo(ifName, ifInfo) + app.convertInternalToOCIntfVlanListInfo(ifName, ifInfo) + app.convertInternalToOCIntfIPAttrInfo(ifName, ifInfo) + app.convertInternalToOCPortStatInfo(ifName, ifInfo) +} + +/* Build tree for sending the response back to North bound */ +func (app *IntfApp) processBuildTree(ifInfo *ocbinds.OpenconfigInterfaces_Interfaces_Interface, ifKey *string) { + ygot.BuildEmptyTree(ifInfo) + if *app.ygotTarget == ifInfo.State { + ygot.BuildEmptyTree(ifInfo.State) + } + app.convertInternalToOCIntfInfo(ifKey, ifInfo) +} + +func (app *IntfApp) processGetSpecificIntf(dbs [db.MaxDB]*db.DB, targetUriPath *string) (GetResponse, error) { + var err error + var payload []byte + var ok bool + var resp *GetResponse + + pathInfo := app.path + intfObj := app.getAppRootObject() + + log.Infof("Received GET for path %s; template: %s vars=%v", pathInfo.Path, pathInfo.Template, pathInfo.Vars) + + if intfObj.Interface != nil && len(intfObj.Interface) > 0 { + /* Interface name is the key */ + for ifKey, _ := range intfObj.Interface { + log.Info("Interface Name = ", ifKey) + err = app.getIntfTypeFromIntf(&ifKey) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + switch app.intfType { + case ETHERNET: + /* First, convert the data to the DS and check for the request type */ + err = app.processGetConvertDBPhyIfInfoToDS(&ifKey) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + ok, resp, err = app.processGetSpecificAttr(targetUriPath, &ifKey) + if ok { + return *resp, err + } + + case VLAN: + err = app.processGetConvertDBVlanIfInfoToDS(&ifKey) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + ok, resp, err := app.processGetSpecificAttr(targetUriPath, &ifKey) + if ok { + return *resp, err + } + case LAG: + err = app.processGetConvertDBLagIfInfoToDS(&ifKey) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + ok, resp, err := app.processGetSpecificAttr(targetUriPath, &ifKey) + if ok { + return *resp, err + } + } + + ifInfo := intfObj.Interface[ifKey] + /* Attribute level handling is done at the top and returned. Any container level handling + needs to be updated here. This is done, just to avoid un-necessary building of tree for any + incoming request */ + /* TODO: Need to handle these conditions in a cleaner way */ + if *app.ygotTarget != ifInfo && *app.ygotTarget != ifInfo.Config && *app.ygotTarget != ifInfo.State && + *app.ygotTarget != ifInfo.State.Counters { + return GetResponse{Payload: payload}, errors.New("Requested get type not supported!") + } + app.processBuildTree(ifInfo, &ifKey) + + if *app.ygotTarget == ifInfo { + payload, err = dumpIetfJson(intfObj, false) + } else { + dummyifInfo := &ocbinds.OpenconfigInterfaces_Interfaces_Interface{} + if *app.ygotTarget == ifInfo.Config { + dummyifInfo.Config = ifInfo.Config + payload, err = dumpIetfJson(dummyifInfo, false) + } else if *app.ygotTarget == ifInfo.State { + dummyifInfo.State = ifInfo.State + payload, err = dumpIetfJson(dummyifInfo, false) + } else if *app.ygotTarget == ifInfo.State.Counters { + dummyifStateInfo := &ocbinds.OpenconfigInterfaces_Interfaces_Interface_State{} + dummyifStateInfo.Counters = ifInfo.State.Counters + payload, err = dumpIetfJson(dummyifStateInfo, false) + } else { + log.Info("Not supported get type!") + err = errors.New("Requested get-type not supported!") + } + } + resp = &(GetResponse{Payload: payload}) + } + } + return *resp, err +} + +func (app *IntfApp) processGetAllInterfaces(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + var err error + var payload []byte + var resp *GetResponse + + ifName := "" + intfObj := app.getAppRootObject() + + log.Info("Get all Interfaces request!") + + err = app.processGetConvertDBIfInfoToDS(&ifName) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + ygot.BuildEmptyTree(intfObj) + for ifName, _ := range app.ifTableMap { + ifInfo, err := intfObj.NewInterface(ifName) + if err != nil { + log.Errorf("Creation of interface subtree for %s failed!", ifName) + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + app.processBuildTree(ifInfo, &ifName) + } + if *app.ygotTarget == intfObj { + payload, err = dumpIetfJson((*app.ygotRoot).(*ocbinds.Device), true) + resp = &(GetResponse{Payload: payload}) + } else { + log.Error("Wrong Request!") + } + return *resp, err +} diff --git a/src/translib/intf_utils.go b/src/translib/intf_utils.go new file mode 100644 index 0000000000..fd9484d80e --- /dev/null +++ b/src/translib/intf_utils.go @@ -0,0 +1,522 @@ +////////////////////////////////////////////////////////////////////////// +// +// 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" + log "github.com/golang/glog" + "net" + "regexp" + "strconv" + "strings" + "translib/db" + "translib/tlerr" +) + +/* Extract Interface type from Interface name */ +func (app *IntfApp) getIntfTypeFromIntf(ifName *string) error { + var err error + + if len(*ifName) == 0 { + return errors.New("Interface name received is empty! Fetching if-type from interface failed!") + } + if strings.HasPrefix(*ifName, "Ethernet") { + app.intfType = ETHERNET + } else if strings.HasPrefix(*ifName, "Vlan") { + app.intfType = VLAN + } else if strings.HasPrefix(*ifName, "PortChannel") { + app.intfType = LAG + } else { + return errors.New("Fetching Interface type from Interface name failed!") + } + return err +} + +/* Validates whether the specific IP exists in the DB for an Interface*/ +func (app *IntfApp) validateIp(dbCl *db.DB, ifName string, ip string, ts *db.TableSpec) error { + app.allIpKeys, _ = app.doGetAllIpKeys(dbCl, ts) + + for _, key := range app.allIpKeys { + if len(key.Comp) < 2 { + continue + } + if key.Get(0) != ifName { + continue + } + ipAddr, _, _ := net.ParseCIDR(key.Get(1)) + ipStr := ipAddr.String() + if ipStr == ip { + log.Infof("IP address %s exists, updating the DS for deletion!", ipStr) + ipInfo, err := dbCl.GetEntry(ts, key) + if err != nil { + log.Error("Error found on fetching Interface IP info from App DB for Interface Name : ", ifName) + return err + } + if len(app.ifIPTableMap[key.Get(0)]) == 0 { + app.ifIPTableMap[key.Get(0)] = make(map[string]dbEntry) + app.ifIPTableMap[key.Get(0)][key.Get(1)] = dbEntry{entry: ipInfo} + } else { + app.ifIPTableMap[key.Get(0)][key.Get(1)] = dbEntry{entry: ipInfo} + } + return nil + } + } + return errors.New(fmt.Sprintf("IP address : %s doesn't exist!", ip)) +} + +/* Validate whether the Interface has IP configuration */ +func (app *IntfApp) validateIpCfgredForInterface(dbCl *db.DB, ifName *string) bool { + app.allIpKeys, _ = app.doGetAllIpKeys(dbCl, app.intfD.intfIPTs) + + for _, key := range app.allIpKeys { + if len(key.Comp) < 2 { + continue + } + if key.Get(0) == *ifName { + return false + } + } + return true +} + +/* Check for IP overlap */ +func (app *IntfApp) translateIpv4(d *db.DB, intf string, ip string, prefix int) error { + var err error + var ifsKey db.Key + + ifsKey.Comp = []string{intf} + + ipPref := ip + "/" + strconv.Itoa(prefix) + ifsKey.Comp = []string{intf, ipPref} + + log.Info("ifsKey:=", ifsKey) + + log.Info("Checking for IP overlap ....") + ipA, ipNetA, _ := net.ParseCIDR(ipPref) + + for _, key := range app.allIpKeys { + if len(key.Comp) < 2 { + continue + } + ipB, ipNetB, _ := net.ParseCIDR(key.Get(1)) + + if ipNetA.Contains(ipB) || ipNetB.Contains(ipA) { + log.Info("IP ", ipPref, "overlaps with ", key.Get(1), " of ", key.Get(0)) + + if intf != key.Get(0) { + //IP overlap across different interface, reject + log.Error("IP ", ipPref, " overlaps with ", key.Get(1), " of ", key.Get(0)) + + errStr := "IP " + ipPref + " overlaps with IP " + key.Get(1) + " of Interface " + key.Get(0) + err = tlerr.InvalidArgsError{Format: errStr} + return err + } else { + //IP overlap on same interface, replace + var entry dbEntry + entry.op = opDelete + + log.Info("Entry ", key.Get(1), " on ", intf, " needs to be deleted") + if app.ifIPTableMap[intf] == nil { + app.ifIPTableMap[intf] = make(map[string]dbEntry) + } + app.ifIPTableMap[intf][key.Get(1)] = entry + } + } + } + + //At this point, we need to add the entry to db + { + var entry dbEntry + entry.op = opCreate + + m := make(map[string]string) + m["NULL"] = "NULL" + value := db.Value{Field: m} + entry.entry = value + if app.ifIPTableMap[intf] == nil { + app.ifIPTableMap[intf] = make(map[string]dbEntry) + } + app.ifIPTableMap[intf][ipPref] = entry + } + return err +} + +/* Validate whether VLAN exists in DB */ +func (app *IntfApp) validateVlanExists(d *db.DB, vlanName *string) error { + if len(*vlanName) == 0 { + return errors.New("Length of VLAN name is zero") + } + entry, err := d.GetEntry(app.vlanD.vlanTs, db.Key{Comp: []string{*vlanName}}) + if err != nil || !entry.IsPopulated() { + errStr := "Invalid Vlan:" + *vlanName + return errors.New(errStr) + } + return nil +} + +/* Validate whether LAG exists in DB */ +func (app *IntfApp) validateLagExists(d *db.DB, lagName *string) error { + if len(*lagName) == 0 { + return errors.New("Length of Lag name is zero") + } + entry, err := d.GetEntry(app.lagD.lagTs, db.Key{Comp: []string{*lagName}}) + log.Info("Lag Entry found:", entry) + if err != nil || !entry.IsPopulated() { + errStr := "Invalid Lag:" + *lagName + return errors.New(errStr) + } + return nil +} + +/* Validate whether physical interface is valid or not */ +/* TODO: This needs to be extended based on Interface type */ +func (app *IntfApp) validateInterface(dbCl *db.DB, ifName string, ifKey db.Key) error { + var err error + if len(ifName) == 0 { + return errors.New("Empty Interface name") + } + + _, err = dbCl.GetEntry(app.intfD.portTblTs, ifKey) + if err != nil { + log.Errorf("Error found on fetching Interface info from App DB for If Name : %s", ifName) + errStr := "Invalid Interface:" + ifName + err = tlerr.InvalidArgsError{Format: errStr} + return err + } + return err +} + +/* Generate Member Ports string from Slice to update VLAN table in CONFIG DB */ +func generateMemberPortsStringFromSlice(memberPortsList []string) *string { + if len(memberPortsList) == 0 { + return nil + } + var memberPortsStr strings.Builder + idx := 1 + + for _, memberPort := range memberPortsList { + if idx != len(memberPortsList) { + memberPortsStr.WriteString(memberPort + ",") + } else { + memberPortsStr.WriteString(memberPort) + } + idx = idx + 1 + } + memberPorts := memberPortsStr.String() + return &(memberPorts) +} + +/* Generate list of member-ports from string */ +func generateMemberPortsSliceFromString(memberPortsStr *string) []string { + if len(*memberPortsStr) == 0 { + return nil + } + memberPorts := strings.Split(*memberPortsStr, ",") + return memberPorts +} + +/* Extract VLAN-Id from Vlan String */ +func getVlanIdFromVlanName(vlanName *string) (string, error) { + if !strings.HasPrefix(*vlanName, "Vlan") { + return "", errors.New("Not valid vlan name : " + *vlanName) + } + id := strings.SplitAfter(*vlanName, "Vlan") + log.Info("Extracted VLAN-Id = ", id[1]) + return id[1], nil +} + +/* Convert tagging mode to Interface Mode type */ +func convertTaggingModeToInterfaceModeType(tagMode *string, ifMode *intfModeType) { + switch *tagMode { + case "untagged": + *ifMode = ACCESS + case "tagged": + *ifMode = TRUNK + } +} + +/* Validate whether member port exists in the member ports list and return the configured Interface mode */ +func checkMemberPortExistsInListAndGetMode(d *db.DB, memberPortsList []string, memberPort *string, vlanName *string, ifMode *intfModeType) bool { + for _, port := range memberPortsList { + if *memberPort == port { + tagModeEntry, err := d.GetEntry(&db.TableSpec{Name: "VLAN_MEMBER"}, db.Key{Comp: []string{*vlanName, *memberPort}}) + if err != nil { + return false + } + tagMode := tagModeEntry.Field["tagging_mode"] + convertTaggingModeToInterfaceModeType(&tagMode, ifMode) + return true + } + } + return false +} + +/* Validate IPv4 address */ +func validIPv4(ipAddress string) bool { + ipAddress = strings.Trim(ipAddress, " ") + + re, _ := regexp.Compile(`^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$`) + if re.MatchString(ipAddress) { + return true + } + return false +} + +/* Validate IPv6 address */ +func validIPv6(ip6Address string) bool { + ip6Address = strings.Trim(ip6Address, " ") + re, _ := regexp.Compile(`(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))`) + if re.MatchString(ip6Address) { + return true + } + return false +} + +/* Get all the IP keys from INTERFACE table */ +func (app *IntfApp) doGetAllIpKeys(d *db.DB, dbSpec *db.TableSpec) ([]db.Key, error) { + + var keys []db.Key + + intfTable, err := d.GetTable(dbSpec) + if err != nil { + return keys, err + } + + keys, err = intfTable.GetKeys() + log.Infof("Found %d INTF table keys", len(keys)) + return keys, err +} + +/* Removal of untagged-vlan associated with interface, updates VLAN_MEMBER table and returns vlan*/ +func (app *IntfApp) removeUntaggedVlanAndUpdateVlanMembTbl(d *db.DB, ifName *string) (*string, error) { + if len(*ifName) == 0 { + return nil, errors.New("Interface name is empty for fetching list of VLANs!") + } + + var vlanMemberKeys []db.Key + vlanMemberTable, err := d.GetTable(app.vlanD.vlanMemberTs) + if err != nil { + return nil, err + } + + vlanMemberKeys, err = vlanMemberTable.GetKeys() + log.Infof("Found %d Vlan Member table keys", len(vlanMemberKeys)) + + for _, vlanMember := range vlanMemberKeys { + if len(vlanMember.Comp) < 2 { + continue + } + if vlanMember.Get(1) != *ifName { + continue + } + memberPortEntry, err := d.GetEntry(app.vlanD.vlanMemberTs, vlanMember) + if err != nil || !memberPortEntry.IsPopulated() { + errStr := "Get from VLAN_MEMBER table for Vlan: + " + vlanMember.Get(0) + " Interface:" + *ifName + " failed!" + return nil, errors.New(errStr) + } + tagMode, ok := memberPortEntry.Field["tagging_mode"] + if !ok { + errStr := "tagging_mode entry is not present for VLAN: " + vlanMember.Get(0) + " Interface: " + *ifName + return nil, errors.New(errStr) + } + + vlanName := vlanMember.Get(0) + if tagMode == "untagged" { + err = d.DeleteEntry(app.vlanD.vlanMemberTs, db.Key{Comp: []string{vlanMember.Get(0), *ifName}}) + if err != nil { + return nil, err + } + // Disable STP configuration for ports which are removed from VLan membership + var memberPorts []string + memberPorts = append(memberPorts, *ifName) + removeStpOnInterfaceSwitchportDeletion(d, memberPorts) + + return &vlanName, nil + } + } + errStr := "Untagged VLAN configuration doesn't exist for Interface: " + *ifName + return nil, tlerr.InvalidArgsError{Format: errStr} +} + +/* Removal of tagged-vlan associated with interface and update VLAN_MEMBER table */ +func (app *IntfApp) removeTaggedVlanAndUpdateVlanMembTbl(d *db.DB, trunkVlan *string, ifName *string) error { + var err error + memberPortEntry, err := d.GetEntry(app.vlanD.vlanMemberTs, db.Key{Comp: []string{*trunkVlan, *ifName}}) + if err != nil || !memberPortEntry.IsPopulated() { + errStr := "Trunk Vlan: " + *trunkVlan + " not configured for Interface: " + *ifName + return errors.New(errStr) + } + tagMode, ok := memberPortEntry.Field["tagging_mode"] + if !ok { + errStr := "tagging_mode entry is not present for VLAN: " + *trunkVlan + " Interface: " + *ifName + return errors.New(errStr) + } + vlanName := *trunkVlan + if tagMode == "tagged" { + err = d.DeleteEntry(app.vlanD.vlanMemberTs, db.Key{Comp: []string{*trunkVlan, *ifName}}) + if err != nil { + return err + } + // Disable STP configuration for ports which are removed from VLan membership + var memberPorts []string + memberPorts = append(memberPorts, *ifName) + removeStpOnInterfaceSwitchportDeletion(d, memberPorts) + } else { + vlanId := vlanName[len("Vlan"):len(vlanName)] + errStr := "Tagged VLAN: " + vlanId + " configuration doesn't exist for Interface: " + *ifName + return tlerr.InvalidArgsError{Format: errStr} + } + return err +} + +/* Validate whether Port has any Untagged VLAN Config existing */ +func (app *IntfApp) validateUntaggedVlanCfgredForIf(d *db.DB, ifName *string, accessVlan *string) (bool, error) { + var err error + + var vlanMemberKeys []db.Key + vlanMemberTable, err := d.GetTable(app.vlanD.vlanMemberTs) + if err != nil { + return false, err + } + + vlanMemberKeys, err = vlanMemberTable.GetKeys() + log.Infof("Found %d Vlan Member table keys", len(vlanMemberKeys)) + + for _, vlanMember := range vlanMemberKeys { + if len(vlanMember.Comp) < 2 { + continue + } + if vlanMember.Get(1) != *ifName { + continue + } + memberPortEntry, err := d.GetEntry(app.vlanD.vlanMemberTs, vlanMember) + if err != nil || !memberPortEntry.IsPopulated() { + errStr := "Get from VLAN_MEMBER table for Vlan: + " + vlanMember.Get(0) + " Interface:" + *ifName + " failed!" + return false, errors.New(errStr) + } + tagMode, ok := memberPortEntry.Field["tagging_mode"] + if !ok { + errStr := "tagging_mode entry is not present for VLAN: " + vlanMember.Get(0) + " Interface: " + *ifName + return false, errors.New(errStr) + } + if tagMode == "untagged" { + *accessVlan = vlanMember.Get(0) + return true, nil + } + } + return false, nil +} + +/* Removes all VLAN_MEMBER table entries for Interface and Get list of VLANs */ +func (app *IntfApp) removeAllVlanMembrsForIfAndGetVlans(d *db.DB, ifName *string, ifMode intfModeType) ([]string, error) { + var err error + var vlanKeys []db.Key + vlanTable, err := d.GetTable(app.vlanD.vlanMemberTs) + if err != nil { + return nil, err + } + + vlanKeys, err = vlanTable.GetKeys() + var vlanSlice []string + + for _, vlanKey := range vlanKeys { + if len(vlanKeys) < 2 { + continue + } + if vlanKey.Get(1) == *ifName { + entry, err := d.GetEntry(app.vlanD.vlanMemberTs, vlanKey) + if err != nil { + log.Errorf("Error found on fetching Vlan member info from App DB for Interface Name : %s", *ifName) + return vlanSlice, err + } + tagInfo, ok := entry.Field["tagging_mode"] + if ok { + switch ifMode { + case ACCESS: + if tagInfo != "tagged" { + continue + } + case TRUNK: + if tagInfo != "untagged" { + continue + } + } + vlanSlice = append(vlanSlice, vlanKey.Get(0)) + d.DeleteEntry(app.vlanD.vlanMemberTs, vlanKey) + } + } + } + return vlanSlice, err +} + +/* Removes the Interface name from Members list of VLAN table and updates it */ +func (app *IntfApp) removeFromMembersListForVlan(d *db.DB, vlan *string, ifName *string) error { + + vlanEntry, err := d.GetEntry(app.vlanD.vlanTs, db.Key{Comp: []string{*vlan}}) + if err != nil { + log.Errorf("Get Entry for VLAN table with Vlan:%s failed!", *vlan) + return err + } + memberPortsInfo, ok := vlanEntry.Field["members@"] + if ok { + memberPortsList := generateMemberPortsSliceFromString(&memberPortsInfo) + if memberPortsList == nil { + return nil + } + idx := 0 + memberFound := false + + for idxVal, memberName := range memberPortsList { + if memberName == *ifName { + memberFound = true + idx = idxVal + break + } + } + if memberFound { + memberPortsList = append(memberPortsList[:idx], memberPortsList[idx+1:]...) + if len(memberPortsList) == 0 { + log.Info("Deleting the members@") + delete(vlanEntry.Field, "members@") + } else { + memberPortsStr := generateMemberPortsStringFromSlice(memberPortsList) + log.Infof("Updated Member ports = %s for VLAN: %s", *memberPortsStr, *vlan) + vlanEntry.Field["members@"] = *memberPortsStr + } + d.SetEntry(app.vlanD.vlanTs, db.Key{Comp: []string{*vlan}}, vlanEntry) + } else { + return nil + } + } + return nil +} + +/* Removes Interface name from Members-list for all VLANs from VLAN table and updates it */ +func (app *IntfApp) removeFromMembersListForAllVlans(d *db.DB, ifName *string, vlanSlice []string) error { + var err error + + for _, vlan := range vlanSlice { + err = app.removeFromMembersListForVlan(d, &vlan, ifName) + if err != nil { + return err + } + } + return err +} diff --git a/src/translib/lag_intf.go b/src/translib/lag_intf.go new file mode 100644 index 0000000000..7c112df910 --- /dev/null +++ b/src/translib/lag_intf.go @@ -0,0 +1,243 @@ +////////////////////////////////////////////////////////////////////////// +// +// 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" + log "github.com/golang/glog" + "strconv" + "translib/db" + "translib/ocbinds" + "translib/tlerr" +) + +/******** CONFIG FUNCTIONS ********/ + +func (app *IntfApp) translateUpdateLagIntfConfig(d *db.DB, lagName *string, lag *ocbinds.OpenconfigInterfaces_Interfaces_Interface) error { + var err error + m := make(map[string]string) + entryVal := db.Value{Field: m} + curr, _ := d.GetEntry(app.lagD.lagTs, db.Key{Comp: []string{*lagName}}) + // Create new PortChannel entry + if !curr.IsPopulated() { + log.Info(*lagName + " not present in DB, need to create it!!") + entryVal.Field["admin_status"] = "up" + entryVal.Field["mtu"] = "9100" + app.ifTableMap[*lagName] = dbEntry{op: opCreate, entry: entryVal} + return nil + } + // PortChannel already exists, update entries + if (lag.Aggregation) != nil { + if (lag.Aggregation.Config) == nil { + return err + } + if lag.Aggregation.Config.MinLinks != nil { + curr.Field["min_links"] = strconv.Itoa(int(*lag.Aggregation.Config.MinLinks)) + } + if lag.Aggregation.Config.Fallback != nil { + curr.Field["fallback"] = strconv.FormatBool(*lag.Aggregation.Config.Fallback) + } + } + app.translateUpdateIntfConfig(lagName, lag, &curr) + return err +} + +func (app *IntfApp) translateUpdateLagIntf(d *db.DB, lagName *string, inpOp reqType) ([]db.WatchKeys, error) { + + var err error + var keys []db.WatchKeys + + intfObj := app.getAppRootObject() + intf := intfObj.Interface[*lagName] + + /* Handling Interface attrbutes config updates */ + app.translateUpdateLagIntfConfig(d, lagName, intf) + if err != nil { + return keys, err + } + + /* Handling Interface IP address updates */ + err = app.translateUpdateIntfSubInterfaces(d, lagName, intf) + if err != nil { + return keys, err + } + return keys, err +} + +func (app *IntfApp) processUpdateLagIntfConfig(d *db.DB) error { + var err error + for lagName, lagEntry := range app.ifTableMap { + switch lagEntry.op { + case opCreate: + err = d.CreateEntry(app.lagD.lagTs, db.Key{Comp: []string{lagName}}, lagEntry.entry) + if err != nil { + errStr := "Creating LAG entry for LAG : " + lagName + " failed" + return errors.New(errStr) + } + case opUpdate: + err = d.SetEntry(app.lagD.lagTs, db.Key{Comp: []string{lagName}}, lagEntry.entry) + if err != nil { + errStr := "Updating LAG entry for LAG : " + lagName + " failed" + return errors.New(errStr) + } + } + } + return err +} + +func (app *IntfApp) processUpdateLagIntf(d *db.DB) error { + var err error + err = app.processUpdateLagIntfConfig(d) + if err != nil { + return err + } + + err = app.processUpdateIntfSubInterfaces(d) + if err != nil { + return err + } + + return err +} + +/********* DELETE FUNCTIONS ********/ +func (app *IntfApp) translateDeleteLagIntface(d *db.DB, intf *ocbinds.OpenconfigInterfaces_Interfaces_Interface, lagName *string) error { + var err error + curr, err := d.GetEntry(app.lagD.lagTs, db.Key{Comp: []string{*lagName}}) + if err != nil { + errStr := "Invalid Lag: " + *lagName + return tlerr.InvalidArgsError{Format: errStr} + } + app.ifTableMap[*lagName] = dbEntry{entry: curr, op: opDelete} + return err +} + +func (app *IntfApp) translateDeleteLagIntf(d *db.DB, ifName *string) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + + intfObj := app.getAppRootObject() + intf := intfObj.Interface[*ifName] + + if intf.Subinterfaces != nil { //Only remove IP entry + err = app.translateDeleteIntfSubInterfaces(d, intf, ifName) + if err != nil { + return keys, err + } + return keys, err + } + + /* Handling PortChannel Deletion */ + err = app.translateDeleteLagIntface(d, intf, ifName) + if err != nil { + return keys, err + } + + return keys, err +} + +/* Delete will require updating both PORTCHANNEL, PORTCHANNEL_MEMBER TABLE, PORTCHANNEL_INTERFACE TABLE */ +func (app *IntfApp) processDeleteLagIntfAndMembers(d *db.DB) error { + var err error + + for lagKey, _ := range app.ifTableMap { + log.Info("lagKey is", lagKey) + lagKeys, err1 := d.GetKeys(app.lagD.lagMemberTs) + lagIPKeys, err2 := d.GetKeys(app.lagD.lagIPTs) + /* Delete entries in PORTCHANNEL_MEMBER TABLE */ + if err1 == nil { + for i, _ := range lagKeys { + if lagKey == lagKeys[i].Get(0) { + log.Info("Removing member port", lagKeys[i].Get(1)) + err = d.DeleteEntry(app.lagD.lagMemberTs, lagKeys[i]) + if err != nil { + log.Info("Deleting member port entry failed") + return err + } + } + } + } + /* Delete entry in PORTCHANNEL_INTERFACE TABLE */ + if err2 == nil { + for i := range lagIPKeys { + if lagKey == lagIPKeys[i].Get(0) { + log.Info("the length is", len(lagIPKeys[i].Comp)) + if len(lagIPKeys[i].Comp) < 2 { + continue + } + ifname := lagIPKeys[i].Get(1) + log.Info("Removing IP entry for", ifname) + err = d.DeleteEntry(app.lagD.lagIPTs, lagIPKeys[i]) + if err != nil { + log.Info("Deleting IP address entry failed") + return err + } + } + } + for i := range lagIPKeys { + if len(lagIPKeys[i].Comp) < 2 { + err = d.DeleteEntry(app.lagD.lagIPTs, db.Key{Comp: []string{lagKey}}) + if err != nil { + log.Info("Unable to delete Interface name entry in PORTCHANNEL_INTERFACE TABLE") + return err + } + } + } + } + /* Delete entry in PORTCHANNEL TABLE */ + err = d.DeleteEntry(app.lagD.lagTs, db.Key{Comp: []string{lagKey}}) + if err != nil { + return err + } + log.Info("Success- PortChannel deletion complete") + } + return err +} + +/* Delete entry from PORTCHANNEL_INTERFACE TABLE */ +func (app *IntfApp) processDeleteLagIntfSubInterfaces(d *db.DB) error { + var err error + for ifName, ipEntries := range app.ifIPTableMap { + for ip, _ := range ipEntries { + log.Info("Deleting entry for ", ifName, ":", ip) + err = d.DeleteEntry(app.lagD.lagIPTs, db.Key{Comp: []string{ifName, ip}}) + if err != nil { + return err + } + } + log.Info("Success- IP adddress entry removed") + } + return err +} + +func (app *IntfApp) processDeleteLagIntf(d *db.DB) error { + var err error + + err = app.processDeleteLagIntfSubInterfaces(d) + if err != nil { + return err + } + + err = app.processDeleteLagIntfAndMembers(d) + if err != nil { + return err + } + + return err +} diff --git a/src/translib/lag_intf_test.go b/src/translib/lag_intf_test.go new file mode 100644 index 0000000000..7635ab4c41 --- /dev/null +++ b/src/translib/lag_intf_test.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 translib + +import ( + "errors" + "fmt" + "testing" + "time" + db "translib/db" +) + +func init() { + fmt.Println("+++++ Init PortChannel_Intf_test +++++") +} + +func Test_LagIntfOperations(t *testing.T) { + + fmt.Println("+++++ Start PorChannel Testing +++++") + + url := "/openconfig-interfaces:interfaces/interface[name=PortChannel100]" + time.Sleep(2 * time.Second) + t.Run("Create_PortChannel(PATCH)", processSetRequest(url, emptyJson, "PATCH", false)) + time.Sleep(1 * time.Second) + fmt.Println("+++++ Done PorChannel Creation +++++") + + stateUrl := url + "/state" + t.Run("Verify_PortChannel_Create", processGetRequest(stateUrl, intfStateGetJsonResponse, false)) + + // Set MTU + mtuUrl := url + "/config/mtu" + t.Run("Configure PortChannel MTU", processSetRequest(mtuUrl, mtuJson, "PATCH", false)) + time.Sleep(1 * time.Second) + fmt.Println("+++++ Done PorChannel MTU configuration +++++") + + // Get MTU + stateMtu := stateUrl + "/mtu" + t.Run("Verify_PortChannel_MTU_Set", processGetRequest(stateMtu, mtuJson, false)) + + adminUrl := url + "/config/enabled" + t.Run("Configure PortChannel admin-status", processSetRequest(adminUrl, enabledJson, "PATCH", false)) + time.Sleep(1 * time.Second) + fmt.Println("+++++ Done PorChannel admin-status configuration +++++") + + // Get admin-status + stateAdmin := stateUrl + "/admin-status" + t.Run("Verify_PortChannel_AdminStatus_Set", processGetRequest(stateAdmin, adminJson, false)) + + aggregationUrl := url + "/openconfig-if-aggregate:aggregation" + + // Set min-links + minLinksUrl := aggregationUrl + "/config/min-links" + t.Run("Configure PortChannel min-links", processSetRequest(minLinksUrl, minLinksJson, "PATCH", false)) + fmt.Println("+++++ Done PorChannel min-links configuration +++++") + + // Get min-links + minLinksGetUrl := aggregationUrl + "/state/min-links" + t.Run("Verify_PortChannel_MinLinks_Set", processGetRequest(minLinksGetUrl, minLinksResp, false)) + + // Set fallback + fallbackUrl := aggregationUrl + "/config/dell-intf-augments:fallback" + t.Run("Configure PortChannel Fallback", processSetRequest(fallbackUrl, fallbackJson, "PATCH", false)) + + // Get fallback mode + fallbackGetUrl := aggregationUrl + "/state/dell-intf-augments:fallback" + t.Run("Verify_portchannel_Fallback_Set", processGetRequest(fallbackGetUrl, fallbackResp, false)) + + // Set IP address + ipUrl := url + "/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv4/addresses/address[ip=11.1.1.1]/config" + t.Run("Configure PortChannel IPv4 address", processSetRequest(ipUrl, ipJson, "PATCH", false)) + fmt.Println("+++++ Done PorChannel IPv4 configuration +++++") + + // Set IPv6 address + ip6Url := url + "/subinterfaces/subinterface[index=0]/openconfig-if-ip:ipv6/addresses/address[ip=a::e]/config" + t.Run("Configure PortChannel IPv6 address", processSetRequest(ip6Url, ip6Json, "PATCH", false)) + fmt.Println("+++++ Done PorChannel IPv6 configuration +++++") + + // Add member ports + memUrl := "/openconfig-interfaces:interfaces/interface[name=Ethernet4]" + "/openconfig-if-ethernet:ethernet/config/openconfig-if-aggregate:aggregate-id" + t.Run("Configure PortChannel Member Add", processSetRequest(memUrl, memAddJson, "PATCH", false)) + + // Get member ports + memGetUrl := aggregationUrl + "/state/member" + t.Run("Verify_PortChannel_Member_Add", processGetRequest(memGetUrl, memRespJson, false)) + + // Remove member ports + t.Run("PortChannel_Member_Remove", processDeleteRequest(memUrl)) + time.Sleep(1 * time.Second) + + // Delete PortChannel + t.Run("Delete_PortChannel", processDeleteRequest(url)) + + time.Sleep(2 * time.Second) + t.Run("Verify_PortChannel_Delete", processGetRequest(stateUrl, "", true)) +} + +// This will delete configs in PortChannel table, PORTCHANNEL_MEMBER table and PORTCHANNEL_INTERFACE table +func clearLagDataFromDb() error { + var err error + lagTable := db.TableSpec{Name: "PORTCHANNEL"} + memTable := db.TableSpec{Name: "PORTCHANNEL_MEMBER"} + IpTable := db.TableSpec{Name: "PORTCHANNEL_INTERFACE"} + + d := getConfigDb() + if d == nil { + err = errors.New("Failed to connect to config Db") + return err + } + if err = d.DeleteTable(&lagTable); err != nil { + err = errors.New("Failed to clear PORTCHANNELTable") + return err + } + if err = d.DeleteTable(&memTable); err != nil { + err = errors.New("Failed to clear PORTCHANNEL_MEMBER Table") + return err + } + if err = d.DeleteTable(&IpTable); err != nil { + err = errors.New("Failed to clear PORTCHANNEL_INTERFACE Table") + return err + } + return err +} + +/***************************************************************************/ +/////////// JSON Data for Tests /////////////// +/***************************************************************************/ + +var intfStateGetJsonResponse string = "{\"openconfig-interfaces:state\":{\"admin-status\":\"UP\",\"mtu\":9100,\"name\":\"PortChannel100\",\"oper-status\":\"DOWN\"}}" + +var mtuJson string = "{\"openconfig-interfaces:mtu\":9000}" + +var enabledJson string = "{\"openconfig-interfaces:enabled\":false}" +var adminJson string = "{\"openconfig-interfaces:admin-status\":\"DOWN\"}" + +var minLinksJson string = "{\"openconfig-if-aggregate:min-links\":1}" +var minLinksResp string = "{\"min-links\":1}" + +var fallbackJson string = "{\"dell-intf-augments:fallback\":true}" +var fallbackResp string = "{\"fallback\":true}" + +var ipJson string = "{\"openconfig-if-ip:config\":{\"ip\":\"11.1.1.1\",\"prefix-length\":24}}" +var ip6Json string = "{\"openconfig-if-ip:config\":{\"ip\":\"a::e\",\"prefix-length\":64}}" + +var memAddJson string = "{\"openconfig-if-aggregate:aggregate-id\":\"100\"}" +var memRespJson string = "{\"member\":[\"Ethernet4\"]}" diff --git a/src/translib/lldp_app.go b/src/translib/lldp_app.go new file mode 100644 index 0000000000..f1af7b1c34 --- /dev/null +++ b/src/translib/lldp_app.go @@ -0,0 +1,468 @@ +////////////////////////////////////////////////////////////////////////// +// +// 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 ( + "strconv" + "reflect" + "errors" + "translib/db" + "translib/ocbinds" + "github.com/openconfig/ygot/ygot" + log "github.com/golang/glog" + "strings" + "encoding/hex" + "translib/tlerr" +) + +const ( + LLDP_REMOTE_CAP_ENABLED = "lldp_rem_sys_cap_enabled" + LLDP_REMOTE_SYS_NAME = "lldp_rem_sys_name" + LLDP_REMOTE_PORT_DESC = "lldp_rem_port_desc" + LLDP_REMOTE_CHASS_ID = "lldp_rem_chassis_id" + LLDP_REMOTE_CAP_SUPPORTED = "lldp_rem_sys_cap_supported" + LLDP_REMOTE_PORT_ID_SUBTYPE = "lldp_rem_port_id_subtype" + LLDP_REMOTE_SYS_DESC = "lldp_rem_sys_desc" + LLDP_REMOTE_REM_TIME = "lldp_rem_time_mark" + LLDP_REMOTE_PORT_ID = "lldp_rem_port_id" + LLDP_REMOTE_REM_ID = "lldp_rem_index" + LLDP_REMOTE_CHASS_ID_SUBTYPE = "lldp_rem_chassis_id_subtype" + LLDP_REMOTE_MAN_ADDR = "lldp_rem_man_addr" +) + +type lldpApp struct { + path *PathInfo + ygotRoot *ygot.GoStruct + ygotTarget *interface{} + appDb *db.DB + neighTs *db.TableSpec + lldpTableMap map[string]db.Value + lldpNeighTableMap map[string]map[string]string + lldpCapTableMap map[string]map[string]bool +} + +func init() { + log.Info("Init called for LLDP modules module") + err := register("/openconfig-lldp:lldp", + &appInfo{appType: reflect.TypeOf(lldpApp{}), + ygotRootType: reflect.TypeOf(ocbinds.OpenconfigLldp_Lldp{}), + isNative: false}) + if err != nil { + log.Fatal("Register LLDP app module with App Interface failed with error=", err) + } + + err = addModel(&ModelData{Name: "openconfig-lldp", + Org: "OpenConfig working group", + Ver: "1.0.2"}) + if err != nil { + log.Fatal("Adding model data to appinterface failed with error=", err) + } +} + +func (app *lldpApp) initialize(data appData) { + log.Info("initialize:lldp:path =", data.path) + *app = lldpApp{path: NewPathInfo(data.path), ygotRoot: data.ygotRoot, ygotTarget: data.ygotTarget} + app.neighTs = &db.TableSpec{Name: "LLDP_ENTRY_TABLE"} + app.lldpTableMap = make(map[string]db.Value) + app.lldpNeighTableMap = make(map[string]map[string]string) + app.lldpCapTableMap = make(map[string]map[string]bool) +} + +func (app *lldpApp) getAppRootObject() (*ocbinds.OpenconfigLldp_Lldp) { + deviceObj := (*app.ygotRoot).(*ocbinds.Device) + return deviceObj.Lldp +} + +func (app *lldpApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCreate:lldp:path =", app.path) + + err = errors.New("Not implemented") + return keys, err +} + +func (app *lldpApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateUpdate:lldp:path =", app.path) + + err = errors.New("Not implemented") + return keys, err +} + +func (app *lldpApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateReplace:lldp:path =", app.path) + + err = errors.New("Not implemented") + return keys, err +} + +func (app *lldpApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateDelete:lldp:path =", app.path) + + err = errors.New("Not implemented") + return keys, err +} + +func (app *lldpApp) translateGet(dbs [db.MaxDB]*db.DB) error { + var err error + log.Info("translateGet:lldp:path = ", app.path) + + return err +} + +func (app *lldpApp) translateAction(dbs [db.MaxDB]*db.DB) error { + err := errors.New("Not supported") + return err +} + +func (app *lldpApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + pathInfo := NewPathInfo(path) + notifInfo := notificationInfo{dbno: db.ApplDB} + notSupported := tlerr.NotSupportedError{Format: "Subscribe not supported", Path: path} + + if isSubtreeRequest(pathInfo.Template, "/openconfig-lldp:lldp/interfaces") { + if pathInfo.HasSuffix("/neighbors") || + pathInfo.HasSuffix("/config") || + pathInfo.HasSuffix("/state") { + log.Errorf("Subscribe not supported for %s!", pathInfo.Template) + return nil, nil, notSupported + } + ifKey := pathInfo.Var("name") + if len(ifKey) == 0 { + return nil, nil, errors.New("ifKey given is empty!") + } + log.Info("Interface name = ", ifKey) + if pathInfo.HasSuffix("/interface{}") { + notifInfo.table = db.TableSpec{Name: "LLDP_ENTRY_TABLE"} + notifInfo.key = asKey(ifKey) + notifInfo.needCache = true + return ¬ificationOpts{pType: OnChange}, ¬ifInfo, nil + } + } + return nil, nil, notSupported +} + +func (app *lldpApp) processCreate(d *db.DB) (SetResponse, error) { + var err error + + err = errors.New("Not implemented") + var resp SetResponse + + return resp, err +} + +func (app *lldpApp) processUpdate(d *db.DB) (SetResponse, error) { + var err error + + err = errors.New("Not implemented") + var resp SetResponse + + return resp, err +} + +func (app *lldpApp) processReplace(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + err = errors.New("Not implemented") + + return resp, err +} + +func (app *lldpApp) processDelete(d *db.DB) (SetResponse, error) { + var err error + err = errors.New("Not implemented") + var resp SetResponse + + return resp, err +} + +func (app *lldpApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + var err error + var payload []byte + + app.appDb = dbs[db.ApplDB] + lldpIntfObj := app.getAppRootObject() + + targetUriPath, err := getYangPathFromUri(app.path.Path) + log.Info("lldp processGet") + log.Info("targetUriPath: ", targetUriPath) + + if targetUriPath == "/openconfig-lldp:lldp/interfaces" { + log.Info("Requesting interfaces") + app.getLldpInfoFromDB(nil) + ygot.BuildEmptyTree(lldpIntfObj) + ifInfo := lldpIntfObj.Interfaces + ygot.BuildEmptyTree(ifInfo) + for ifname,_ := range app.lldpNeighTableMap { + oneIfInfo, err := ifInfo.NewInterface(ifname) + if err != nil { + log.Info("Creation of subinterface subtree failed!") + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + ygot.BuildEmptyTree(oneIfInfo) + app.getLldpNeighInfoFromInternalMap(&ifname, oneIfInfo) + if *app.ygotTarget == lldpIntfObj.Interfaces { + payload, err = dumpIetfJson(lldpIntfObj, true) + } else { + log.Info("Wrong request!") + } + + } + } else if targetUriPath == "/openconfig-lldp:lldp/interfaces/interface" { + intfObj := lldpIntfObj.Interfaces + ygot.BuildEmptyTree(intfObj) + if intfObj.Interface != nil && len(intfObj.Interface) > 0 { + for ifname, _ := range intfObj.Interface { + log.Info("if-name = ", ifname) + app.getLldpInfoFromDB(&ifname) + ifInfo := intfObj.Interface[ifname] + ygot.BuildEmptyTree(ifInfo) + app.getLldpNeighInfoFromInternalMap(&ifname, ifInfo) + + if *app.ygotTarget == intfObj.Interface[ifname] { + payload, err = dumpIetfJson(intfObj, true) + if err != nil { + log.Info("Creation of subinterface subtree failed!") + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + } else { + log.Info("Wrong request!") + } + } + } else { + log.Info("No data") + } + } + + return GetResponse{Payload:payload}, err +} + +func (app *lldpApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) { + var resp ActionResponse + err := errors.New("Not implemented") + + return resp, err +} + +/** Helper function to populate JSON response for GET request **/ +func (app *lldpApp) getLldpNeighInfoFromInternalMap(ifName *string, ifInfo *ocbinds.OpenconfigLldp_Lldp_Interfaces_Interface) { + + ngInfo, err := ifInfo.Neighbors.NewNeighbor(*ifName) + if err != nil { + log.Info("Creation of subinterface subtree failed!") + return + } + ygot.BuildEmptyTree(ngInfo) + neighAttrMap:= app.lldpNeighTableMap[*ifName] + for attr, value := range neighAttrMap { + switch attr { + case LLDP_REMOTE_SYS_NAME: + name := new(string) + *name = value + ngInfo.State.SystemName = name + case LLDP_REMOTE_PORT_DESC: + pdescr := new(string) + *pdescr = value + ngInfo.State.PortDescription = pdescr + case LLDP_REMOTE_CHASS_ID: + chId := new (string) + *chId = value + ngInfo.State.ChassisId = chId + case LLDP_REMOTE_PORT_ID_SUBTYPE: + remPortIdTypeVal, err := strconv.Atoi(value) + if err == nil { + ngInfo.State.PortIdType =ocbinds.E_OpenconfigLldp_PortIdType(remPortIdTypeVal) + } + case LLDP_REMOTE_SYS_DESC: + sdesc:= new(string) + *sdesc = value + ngInfo.State.SystemDescription = sdesc + case LLDP_REMOTE_REM_TIME: + /* Ignore Remote System time */ + case LLDP_REMOTE_PORT_ID: + remPortIdPtr := new(string) + *remPortIdPtr = value + ngInfo.State.PortId = remPortIdPtr + case LLDP_REMOTE_REM_ID: + Id := new(string) + *Id = value + ngInfo.State.Id = Id + case LLDP_REMOTE_CHASS_ID_SUBTYPE: + remChassIdTypeVal , err:=strconv.Atoi(value) + if err == nil { + ngInfo.State.ChassisIdType =ocbinds.E_OpenconfigLldp_ChassisIdType(remChassIdTypeVal) + } + case LLDP_REMOTE_MAN_ADDR: + mgmtAdr:= new(string) + *mgmtAdr = value + ngInfo.State.ManagementAddress = mgmtAdr + default: + log.Info("Not a valid attribute!") + } + } + capLst := app.lldpCapTableMap[*ifName] + for capName, enabled := range capLst { + if capName == "Router" { + capInfo, err := ngInfo.Capabilities.NewCapability(6) + if err == nil { + ygot.BuildEmptyTree(capInfo) + capInfo.State.Name = 6 + capInfo.State.Enabled = &enabled + } + } else if capName == "Repeater" { + capInfo, err := ngInfo.Capabilities.NewCapability(5) + if err == nil { + ygot.BuildEmptyTree(capInfo) + capInfo.State.Name = 5 + capInfo.State.Enabled = &enabled + } + } else if capName == "Bridge" { + capInfo, err := ngInfo.Capabilities.NewCapability(3) + if err == nil { + ygot.BuildEmptyTree(capInfo) + capInfo.State.Name = 3 + capInfo.State.Enabled = &enabled + } + } else { + + } + } +} + +/** Helper function to get information from applDB **/ +func (app *lldpApp) getLldpInfoFromDB(ifname *string) { + + lldpTbl, err := app.appDb.GetTable(app.neighTs) + if err != nil { + log.Info("Can't get lldp table") + return + } + + keys, err := lldpTbl.GetKeys() + if err != nil { + log.Info("Can't get lldp keys") + return + } + + + for _, key := range keys { + log.Info("lldp key = ", key.Get(0)) + + lldpEntry, err := app.appDb.GetEntry(app.neighTs, db.Key{Comp: []string{key.Get(0)}}) + if err != nil { + log.Info("can't access neighbor table for key: ", key.Get(0)) + return + } + + if lldpEntry.IsPopulated() { + log.Info("lldp entry populated for key: ", key.Get(0)) + app.lldpTableMap[key.Get(0)] = lldpEntry + } + } + + for _, key := range keys { + if (ifname != nil && key.Get(0) != *ifname) { + continue + } + entryData := app.lldpTableMap[key.Get(0)] + if len(app.lldpNeighTableMap[key.Get(0)]) == 0 { + app.lldpNeighTableMap[key.Get(0)] = make(map[string]string) + } + for lldpAttr := range entryData.Field { + switch lldpAttr { + case LLDP_REMOTE_CAP_ENABLED: + app.getRemoteSysCap(entryData.Get(lldpAttr), key.Get(0), true) + case LLDP_REMOTE_SYS_NAME: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_SYS_NAME] = entryData.Get(lldpAttr) + case LLDP_REMOTE_PORT_DESC: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_PORT_DESC] = entryData.Get(lldpAttr) + case LLDP_REMOTE_CHASS_ID: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_CHASS_ID] = entryData.Get(lldpAttr) + case LLDP_REMOTE_CAP_SUPPORTED: + app.getRemoteSysCap(entryData.Get(lldpAttr), key.Get(0), false) + case LLDP_REMOTE_PORT_ID_SUBTYPE: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_PORT_ID_SUBTYPE] = entryData.Get(lldpAttr) + case LLDP_REMOTE_SYS_DESC: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_SYS_DESC] = entryData.Get(lldpAttr) + case LLDP_REMOTE_REM_TIME: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_REM_TIME] = entryData.Get(lldpAttr) + case LLDP_REMOTE_PORT_ID: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_PORT_ID] = entryData.Get(lldpAttr) + case LLDP_REMOTE_REM_ID: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_REM_ID] = entryData.Get(lldpAttr) + case LLDP_REMOTE_CHASS_ID_SUBTYPE: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_CHASS_ID_SUBTYPE] = entryData.Get(lldpAttr) + case LLDP_REMOTE_MAN_ADDR: + app.lldpNeighTableMap[key.Get(0)][LLDP_REMOTE_MAN_ADDR] = entryData.Get(lldpAttr) + default: + log.Info("Unknown LLDP Attribute") + } + } + } +} + +/** Helper function to get remote system capabilities into a map **/ +func (app *lldpApp) getRemoteSysCap(capb string, ifname string, setCap bool) { + num_str := strings.Split(capb, " ") + byte, _ := hex.DecodeString(num_str[0] + num_str[1]) + sysCap := byte[0] + sysCap |= byte[1] + + log.Info("sysCap: ", sysCap) + + if (sysCap & (128 >> 1)) != 0 { + if app.lldpCapTableMap[ifname] == nil { + app.lldpCapTableMap[ifname] = make(map[string]bool) + app.lldpCapTableMap[ifname]["Repeater"] = false + } + if (setCap) { + log.Info("Repeater ENABLED") + app.lldpCapTableMap[ifname]["Repeater"] = true + } + } + + if (sysCap & (128 >> 2)) != 0 { + if app.lldpCapTableMap[ifname] == nil { + app.lldpCapTableMap[ifname] = make(map[string]bool) + app.lldpCapTableMap[ifname]["Bridge"] = false + } + if (setCap) { + log.Info("Bridge ENABLED") + app.lldpCapTableMap[ifname]["Bridge"] = true + } + } + + if (sysCap & (128 >> 4)) != 0 { + if app.lldpCapTableMap[ifname] == nil { + app.lldpCapTableMap[ifname] = make(map[string]bool) + app.lldpCapTableMap[ifname]["Router"] = false + } + if (setCap) { + log.Info("Router ENABLED") + app.lldpCapTableMap[ifname]["Router"] = true + } + } +} + diff --git a/src/translib/nonyang_app.go.demo b/src/translib/nonyang_app.go.demo new file mode 100644 index 0000000000..17553ce328 --- /dev/null +++ b/src/translib/nonyang_app.go.demo @@ -0,0 +1,412 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "encoding/json" + "fmt" + "reflect" + "translib/db" + "translib/tlerr" + + log "github.com/golang/glog" +) + +// nonYangDemoApp holds all invocation and state information for +// the non-yang demo app +type nonYangDemoApp struct { + // request information + path *PathInfo + reqData []byte + + // DB client to operate on config_db + confDB *db.DB + + // Cahce for read operation + respJSON interface{} +} + +type jsonObject map[string]interface{} +type jsonArray []interface{} + +var ( + vlanTable = &db.TableSpec{Name: "VLAN"} + memberTable = &db.TableSpec{Name: "VLAN_MEMBER"} +) + +func init() { + register( + "/nonyang/", + &appInfo{appType: reflect.TypeOf(nonYangDemoApp{}), + tablesToWatch: []*db.TableSpec{vlanTable, memberTable}, + isNative: true}) +} + +// initialize function prepares this nonYangDemoApp instance +// for a new request handling. +func (app *nonYangDemoApp) initialize(data appData) { + app.path = NewPathInfo(data.path) + app.reqData = data.payload +} + +func (app *nonYangDemoApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + app.confDB = d + return nil, nil +} + +func (app *nonYangDemoApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + return nil, tlerr.NotSupported("Unsuppoted operation") +} + +func (app *nonYangDemoApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + return nil, tlerr.NotSupported("Unsuppoted operation") +} + +func (app *nonYangDemoApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + app.confDB = d + return nil, nil +} + +func (app *nonYangDemoApp) translateGet(dbs [db.MaxDB]*db.DB) error { + return nil +} + +func (app *nonYangDemoApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + err := tlerr.NotSupported("Unsuppoted operation") + return nil, nil, err +} + +func (app *nonYangDemoApp) processCreate(d *db.DB) (SetResponse, error) { + var resp SetResponse + pathInfo := app.path + var err error + + log.Infof("Received CREATE for path %s; vars=%v", pathInfo.Template, pathInfo.Vars) + + switch pathInfo.Template { + case "/nonyang/vlan": + err = app.doCreateVlans() + + case "/nonyang/vlan/{}/member": + err = app.doCreateVlanMembers() + + default: + err = tlerr.NotSupported("Unknown path") + } + + return resp, err +} + +func (app *nonYangDemoApp) processUpdate(d *db.DB) (SetResponse, error) { + var resp SetResponse + return resp, tlerr.NotSupported("Unsuppoted operation") +} + +func (app *nonYangDemoApp) processReplace(d *db.DB) (SetResponse, error) { + var resp SetResponse + return resp, tlerr.NotSupported("Unsuppoted operation") +} + +func (app *nonYangDemoApp) processDelete(d *db.DB) (SetResponse, error) { + var resp SetResponse + pathInfo := app.path + var err error + + log.Infof("Received DELETE for path %s; vars=%v", pathInfo.Template, pathInfo.Vars) + + switch pathInfo.Template { + case "/nonyang/vlan/{}": + err = app.doDeleteVlan() + + case "/nonyang/vlan/{}/member/{}": + err = app.doDeleteVlanMember() + + default: + err = tlerr.NotSupported("Unknown path") + } + + return resp, err +} + +func (app *nonYangDemoApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + app.confDB = dbs[db.ConfigDB] + pathInfo := app.path + var err error + + log.Infof("Received GET for path %s; vars=%v", pathInfo.Template, pathInfo.Vars) + + switch pathInfo.Template { + case "/nonyang/vlan": + err = app.doGetAllVlans() + + case "/nonyang/vlan/{}": + err = app.doGetVlanByID() + + default: + err = tlerr.NotSupported("Unknown path") + } + + var respData []byte + if err == nil && app.respJSON != nil { + respData, err = json.Marshal(app.respJSON) + } + + return GetResponse{Payload: respData}, err +} + +// doGetAllVlans is the handler for "/nonyang/vlan" path +// Loads all vlans and member data from db and prepares +// a json array - each item being one vlan info. +func (app *nonYangDemoApp) doGetAllVlans() error { + log.Infof("in GetAllVlans") + + // Get all vlans from db + t, err := app.confDB.GetTable(vlanTable) + if err != nil { + return err + } + + var allVlansJSON jsonArray + + keys, _ := t.GetKeys() + log.Infof("Found %d VLAN table keys", len(keys)) + for _, key := range keys { + log.Infof("Processing %v", key.Get(0)) + + vlanInfo, _ := t.GetEntry(key) + vlanJSON, err := app.getVlanJSON(&vlanInfo) + if err != nil { + return err + } + + allVlansJSON = append(allVlansJSON, *vlanJSON) + } + + app.respJSON = &allVlansJSON + return nil +} + +// doGetVlanByID is the handler for "/nonyang/vlan/{id}" path. +// Loads data for one vlan and its members and prepares a json +// object. +func (app *nonYangDemoApp) doGetVlanByID() error { + vlanID, _ := app.path.IntVar("id") + log.Infof("in GetVlanByID(), vid=%d", vlanID) + + vlanEntry, err := app.getVlanEntry(vlanID) + if err == nil { + app.respJSON, err = app.getVlanJSON(&vlanEntry) + } + + return err +} + +// getVlanJSON prepares a raw json object for given VLAN table +// entry. Member information are fetched from VLAN_MEMBER table. +func (app *nonYangDemoApp) getVlanJSON(vlanEntry *db.Value) (*jsonObject, error) { + vlanJSON := make(jsonObject) + var memberListJSON jsonArray + + vlanID, _ := vlanEntry.GetInt("vlanid") + vlanName := toVlanName(vlanID) + + log.Infof("Preparing json for vlan %d", vlanID) + + memberPorts := vlanEntry.GetList("members") + log.Infof("%s members = %v", vlanName, memberPorts) + + for _, portName := range memberPorts { + memberJSON := make(jsonObject) + memberJSON["port"] = portName + + memberEntry, err := app.confDB.GetEntry(memberTable, asKey(vlanName, portName)) + if isNotFoundError(err) { + log.Infof("Failed to load VLAN_MEMBER %s,%s; err=%v", vlanName, portName, err) + } else if err != nil { + return nil, err + } else { + memberJSON["mode"] = memberEntry.Get("tagging_mode") + } + + memberListJSON = append(memberListJSON, memberJSON) + } + + vlanJSON["id"] = vlanID + vlanJSON["name"] = vlanName + vlanJSON["members"] = memberListJSON + + return &vlanJSON, nil +} + +// doCreateVlans handles CREATE operation on "/nonyang/vlan" path. +func (app *nonYangDemoApp) doCreateVlans() error { + log.Infof("in doCreateVlans()") + + // vlan creation expects array of vlan ids. + var vlansJSON []int + err := json.Unmarshal(app.reqData, &vlansJSON) + if err != nil { + log.Errorf("Failed to parse input.. err=%v", err) + return tlerr.InvalidArgs("Invalid input") + } + + log.Infof("Receieved %d vlan ids; %v", len(vlansJSON), vlansJSON) + + for _, vid := range vlansJSON { + vlanName := toVlanName(vid) + log.Infof("NEW vlan entry '%s'", vlanName) + + entry := db.Value{Field: make(map[string]string)} + entry.SetInt("vlanid", vid) + err = app.confDB.CreateEntry(vlanTable, asKey(vlanName), entry) + if err != nil { + return err + } + } + + return nil +} + +// doCreateVlanMembers handles CREATE operation on path +// "/nonyang/vlan/{}/member" +func (app *nonYangDemoApp) doCreateVlanMembers() error { + vlanID, _ := app.path.IntVar("id") + log.Infof("in doCreateVlanMembers(), vid=%d", vlanID) + + var memberListJSON []map[string]string + err := json.Unmarshal(app.reqData, &memberListJSON) + if err != nil { + log.Errorf("Failed to parse input.. err=%v", err) + return tlerr.InvalidArgs("Invalid input") + } + + vlanName := toVlanName(vlanID) + vlanEntry, err := app.getVlanEntry(vlanID) + if err != nil { + return err + } + + membersList := vlanEntry.GetList("members") + + for _, memberJSON := range memberListJSON { + log.Infof("Processing member info %v", memberJSON) + + portName, _ := memberJSON["port"] + membersList = append(membersList, portName) + + taggingMode, ok := memberJSON["mode"] + if !ok { + taggingMode = "tagged" + } + + log.Infof("NEW vlan_member entry '%s|%s'; mode=%s", vlanName, portName, taggingMode) + memberEntry := db.Value{Field: make(map[string]string)} + memberEntry.Set("tagging_mode", taggingMode) + err = app.confDB.CreateEntry(memberTable, asKey(vlanName, portName), memberEntry) + if err != nil { + return err + } + } + + // Update the vlan table with new member list + log.Infof("SET vlan entry '%s', members=%v", vlanName, membersList) + vlanEntry.SetList("members", membersList) + err = app.confDB.ModEntry(vlanTable, asKey(vlanName), vlanEntry) + + return err +} + +func (app *nonYangDemoApp) doDeleteVlan() error { + vlanID, _ := app.path.IntVar("id") + log.Infof("in doDeleteVlan(); vid=%d", vlanID) + + vlanName := toVlanName(vlanID) + vlanKey := asKey(vlanName) + vlanEntry, err := app.confDB.GetEntry(vlanTable, vlanKey) + if isNotFoundError(err) { + log.Infof("Vlan %d not found.. nothing to delete", vlanID) + return nil + } else if err != nil { + return err + } + + // Delete VLAN_MEMBER table entry for each member port + for _, portName := range vlanEntry.GetList("members") { + log.Infof("DEL vlan_member entry '%s|%s'", vlanName, portName) + err = app.confDB.DeleteEntry(memberTable, asKey(vlanName, portName)) + if err != nil { + return err + } + } + + // Delete VLAN table entry + log.Infof("DEL vlan entry '%s'", vlanName) + err = app.confDB.DeleteEntry(vlanTable, vlanKey) + + return err +} + +func (app *nonYangDemoApp) doDeleteVlanMember() error { + vlanID, _ := app.path.IntVar("id") + portName := app.path.Var("port") + log.Infof("in doDeleteVlanMember(); vid=%d, member=%s", vlanID, portName) + + vlanName := toVlanName(vlanID) + vlanEntry, err := app.getVlanEntry(vlanID) + if err != nil { + return err + } + + membersList := vlanEntry.GetList("members") + updatedList := removeElement(membersList, portName) + + // Ignore the request if the port is not a member + if len(membersList) == len(updatedList) { + log.Infof("Vlan %d has no member %s", vlanID, portName) + return nil + } + + // Update VLAN entry with new member list + log.Infof("SET vlan entry '%s', members=%v", vlanName, updatedList) + vlanEntry.SetList("members", updatedList) + err = app.confDB.SetEntry(vlanTable, asKey(vlanName), vlanEntry) + if err != nil { + return err + } + + // Delete VLAN_MEMBER entry + log.Infof("DEL vlan_member entry '%s|%s'", vlanName, portName) + err = app.confDB.DeleteEntry(memberTable, asKey(vlanName, portName)) + + return err +} + +func (app *nonYangDemoApp) getVlanEntry(vlanID int) (db.Value, error) { + entry, err := app.confDB.GetEntry(vlanTable, asKey(toVlanName(vlanID))) + if isNotFoundError(err) { + err = tlerr.NotFound("VLAN %v does not exists", vlanID) + } + return entry, err +} + +// toVlanName returns the vlan name for given vlan id. +func toVlanName(vid int) string { + return fmt.Sprintf("Vlan%d", vid) +} diff --git a/src/translib/ocbinds/oc.go b/src/translib/ocbinds/oc.go new file mode 100644 index 0000000000..3d5333decc --- /dev/null +++ b/src/translib/ocbinds/oc.go @@ -0,0 +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 "$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' -not -name '*annot.yang' | sort)" diff --git a/src/translib/path_utils.go b/src/translib/path_utils.go new file mode 100644 index 0000000000..fad938394d --- /dev/null +++ b/src/translib/path_utils.go @@ -0,0 +1,211 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "translib/ocbinds" + + log "github.com/golang/glog" + "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/ygot" + "github.com/openconfig/ygot/ytypes" +) + +// PathInfo structure contains parsed path information. +type PathInfo struct { + Path string + Template string + Vars map[string]string +} + +// HasVar checks if the PathInfo contains given variable. +func (p *PathInfo) HasVar(name string) bool { + _, exists := p.Vars[name] + return exists +} + +// 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] +} + +// IntVar returns the value for a path variable as an int. +// Returns 0 if no such variable exists. Returns an error +// if the value is not an integer. +func (p *PathInfo) IntVar(name string) (int, error) { + val := p.Vars[name] + if len(val) == 0 { + return 0, nil + } + + return strconv.Atoi(val) +} + +// HasPrefix checks if this path template starts with given +// prefix.. Shorthand for strings.HasPrefix(p.Template, s) +func (p *PathInfo) HasPrefix(s string) bool { + return strings.HasPrefix(p.Template, s) +} + +// HasSuffix checks if this path template ends with given +// suffix.. Shorthand for strings.HasSuffix(p.Template, s) +func (p *PathInfo) HasSuffix(s string) bool { + return strings.HasSuffix(p.Template, s) +} + +// 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, ']') + + // Handle duplicate parameter names by suffixing "#N" to it. + // N is the number of occurance of that parameter name. + if info.HasVar(name) { + namePrefix := name + for k := 2; info.HasVar(name); k++ { + name = fmt.Sprintf("%s#%d", namePrefix, k) + } + } + + if len(name) != 0 { + fmt.Fprintf(&template, "{}") + info.Vars[name] = value + } + } + + info.Template = template.String() + + return &info +} + +func readUntil(r *strings.Reader, delim byte) string { + var buff strings.Builder + var escaped bool + + for { + c, err := r.ReadByte() + if err != nil || (c == delim && !escaped) { + break + } else if c == '\\' && !escaped { + escaped = true + } else { + escaped = false + buff.WriteByte(c) + } + } + + return buff.String() +} + +func getParentNode(targetUri *string, deviceObj *ocbinds.Device) (*interface{}, *yang.Entry, error) { + path, err := ygot.StringToPath(*targetUri, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + return nil, nil, err + } + + var pathList []*gnmi.PathElem = path.Elem + + parentPath := &gnmi.Path{} + + for i := 0; i < (len(pathList) - 1); i++ { + pathSlice := strings.Split(pathList[i].Name, ":") + pathList[i].Name = pathSlice[len(pathSlice)-1] + parentPath.Elem = append(parentPath.Elem, pathList[i]) + } + + treeNodeList, err2 := ytypes.GetNode(ygSchema.RootSchema(), deviceObj, parentPath) + if err2 != nil { + return nil, nil, err2 + } + + if len(treeNodeList) == 0 { + return nil, nil, errors.New("Invalid URI") + } + + return &(treeNodeList[0].Data), treeNodeList[0].Schema, nil +} + +func getNodeName(targetUri *string, deviceObj *ocbinds.Device) (string, error) { + path, err := ygot.StringToPath(*targetUri, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + log.Error("Error in uri to path conversion: ", err) + return "", err + } + + pathList := path.Elem + for i := 0; i < len(pathList); i++ { + pathSlice := strings.Split(pathList[i].Name, ":") + pathList[i].Name = pathSlice[len(pathSlice)-1] + } + + treeNodeList, err := ytypes.GetNode(ygSchema.RootSchema(), deviceObj, path) + if err != nil { + log.Error("Error in uri to path conversion: ", err) + return "", err + } + + if len(treeNodeList) == 0 { + return "", errors.New("Invalid URI") + } + + return treeNodeList[0].Schema.Name, nil +} + +func getObjectFieldName(targetUri *string, deviceObj *ocbinds.Device, ygotTarget *interface{}) (string, error) { + parentObjIntf, _, err := getParentNode(targetUri, deviceObj) + if err != nil { + return "", err + } + valObj := reflect.ValueOf(*parentObjIntf).Elem() + parentObjType := reflect.TypeOf(*parentObjIntf).Elem() + + for i := 0; i < parentObjType.NumField(); i++ { + if reflect.ValueOf(*ygotTarget).Kind() == reflect.Ptr && valObj.Field(i).Kind() == reflect.Ptr { + if valObj.Field(i).Pointer() == reflect.ValueOf(*ygotTarget).Pointer() { + return parentObjType.Field(i).Name, nil + } + } else if valObj.Field(i).String() == reflect.ValueOf(*ygotTarget).String() { + return parentObjType.Field(i).Name, nil + } + } + return "", errors.New("Target object not found") +} diff --git a/src/translib/path_utils_test.go b/src/translib/path_utils_test.go new file mode 100644 index 0000000000..b0c7368cb4 --- /dev/null +++ b/src/translib/path_utils_test.go @@ -0,0 +1,229 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "reflect" + "strings" + "testing" + + "translib/ocbinds" +) + +func TestGetParentNode(t *testing.T) { + + tests := []struct { + tid int + targetUri string + appRootType reflect.Type + want string + }{{ + tid: 1, + targetUri: "/openconfig-acl:acl/acl-sets/", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl", + }, { + tid: 2, + targetUri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets", + }, { + tid: 3, + targetUri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/state/description", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet_State", + }} + + for _, tt := range tests { + var deviceObj ocbinds.Device = ocbinds.Device{} + _, err := getRequestBinder(&tt.targetUri, nil, 1, &tt.appRootType).unMarshallUri(&deviceObj) + if err != nil { + t.Error("TestGetParentNode: Error in unmarshalling the URI", err) + } else { + parentNode, _, err := getParentNode(&tt.targetUri, &deviceObj) + if err != nil { + t.Error("TestGetParentNode: Error in getting the parent node: ", err) + } else if parentNode == nil { + t.Error("TestGetParentNode: Error in getting the parent node") + } else if reflect.TypeOf(*parentNode).Elem().Name() != tt.want { + t.Error("TestGetParentNode: Error in getting the parent node: ", reflect.TypeOf(*parentNode).Elem().Name()) + } + } + } +} + +func TestGetNodeName(t *testing.T) { + + tests := []struct { + tid int + targetUri string + appRootType reflect.Type + want string + }{{ + tid: 1, + targetUri: "/openconfig-acl:acl/acl-sets/", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "acl-sets", + }, { + tid: 2, + targetUri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "acl-set", + }, { + tid: 3, + targetUri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/state/description", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "description", + }, { + tid: 4, // Negative test case + targetUri: "/openconfig-acl:acl/acl-sets/acl-set[name=Sample][type=ACL_IPV4]/state/descriptXX", + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "rpc error: code = InvalidArgument desc = no match found in *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_State, for path elem: 0 && tagVal == binder.targetNodePath.Elem[0].Name { + fv := v.Field(i) + workObjIntf = fv.Interface() + break + } + } + } else if ygEntry.IsList() || binder.targetNodeListInst == true { + if treeNodeList, err2 := ytypes.GetNode(ygEntry, *tmpTargetNode, binder.targetNodePath); err2 != nil { + return nil, nil, tlerr.TranslibSyntaxValidationError{StatusCode: 400, ErrorStr: err2} + } else { + if len(treeNodeList) == 0 { + return nil, nil, tlerr.TranslibSyntaxValidationError{StatusCode: 400, ErrorStr: errors.New("Invalid URI")} + } + workObjIntf = treeNodeList[0].Data + } + } + + if workObjIntf != nil { + workObj = &workObjIntf + } else { + return nil, nil, tlerr.TranslibSyntaxValidationError{StatusCode: 400, ErrorStr: errors.New("Target node not found.")} + } + } + + targetObj, ok := (*tmpTargetNode).(ygot.ValidatedGoStruct) + if ok == true { + err := targetObj.Validate(&ytypes.LeafrefOptions{IgnoreMissingData: true}) + if err != nil { + return nil, nil, tlerr.TranslibSyntaxValidationError{StatusCode: 400, ErrorStr: err} + } + } + + default: + if binder.opcode != GET && binder.opcode != DELETE { + return nil, nil, tlerr.TranslibSyntaxValidationError{StatusCode: 400, ErrorStr: errors.New("Unknown HTTP METHOD in the request")} + } + } + + if binder.opcode != UPDATE && binder.opcode != REPLACE { + if err = binder.validateRequest(&deviceObj); err != nil { + return nil, nil, tlerr.TranslibSyntaxValidationError{StatusCode: 400, ErrorStr: err} + } + } + + return ygotRootObj, workObj, nil +} + +func (binder *requestBinder) getUriPath() (*gnmi.Path, error) { + var path *gnmi.Path + var err error + + path, err = ygot.StringToPath(*binder.uri, ygot.StructuredPath, ygot.StringSlicePath) + if err != nil { + log.Error("Error in uri to path conversion: ", err) + return nil, err + } + + return path, nil +} + +func (binder *requestBinder) unMarshallUri(deviceObj *ocbinds.Device) (*interface{}, error) { + if len(*binder.uri) == 0 { + errMsg := errors.New("Error: URI is empty") + log.Error(errMsg) + return nil, errMsg + } + + path, err := binder.getUriPath() + if err != nil { + return nil, err + } else { + binder.pathTmp = path + } + + for _, p := range path.Elem { + pathSlice := strings.Split(p.Name, ":") + p.Name = pathSlice[len(pathSlice)-1] + } + + ygNode, ygEntry, errYg := ytypes.GetOrCreateNode(ygSchema.RootSchema(), deviceObj, path) + + if errYg != nil { + log.Error("Error in creating the target object: ", errYg) + return nil, errYg + } + + switch binder.opcode { + case UPDATE, REPLACE: + if ygEntry.IsList() && reflect.ValueOf(ygNode).Kind() != reflect.Map { + binder.targetNodeListInst = true + } + var pathList []*gnmi.PathElem = path.Elem + + gpath := &gnmi.Path{} + + for i := 0; i < (len(pathList) - 1); i++ { + log.Info("pathList[i] ", pathList[i]) + gpath.Elem = append(gpath.Elem, pathList[i]) + } + + binder.targetNodePath = &gnmi.Path{} + binder.targetNodePath.Elem = append(binder.targetNodePath.Elem, pathList[(len(pathList)-1)]) + + log.Info("modified path is: ", gpath) + + binder.pathTmp = gpath + } + + if (binder.opcode == GET || binder.opcode == DELETE) && (ygEntry.IsLeaf() == false && ygEntry.IsLeafList() == false) { + if err = binder.validateRequest(deviceObj); err != nil { + return nil, err + } + } + + return &ygNode, nil +} diff --git a/src/translib/request_binder_test.go b/src/translib/request_binder_test.go new file mode 100644 index 0000000000..6baaf696a4 --- /dev/null +++ b/src/translib/request_binder_test.go @@ -0,0 +1,602 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "fmt" + "github.com/openconfig/gnmi/proto/gnmi" + "github.com/openconfig/ygot/ygot" + "reflect" + "strings" + "testing" + "translib/ocbinds" +) + +func TestInitSchema(t *testing.T) { + initSchema() +} + +func TestGetRequestBinder(t *testing.T) { + tests := []struct { + uri string + opcode int + payload *[]byte + appRootType reflect.Type + }{{ + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 1, + payload: nil, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + }} + + for _, tt := range tests { + rb := getRequestBinder(&tt.uri, tt.payload, tt.opcode, &tt.appRootType) + if rb == nil { + t.Error("Error in creating the request binder object") + } else if &tt.uri != rb.uri || tt.opcode != rb.opcode || tt.payload != rb.payload || &tt.appRootType != rb.appRootNodeType { + t.Error("Error in creating the request binder object") + } + } +} + +func TestValidateRequest(t *testing.T) { + tests := []struct { + tid int + uri string + opcode int + payload []byte + appRootType reflect.Type + want string //target object name + }{{ + tid: 1, + uri: "", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "Path is empty", + }, { + tid: 2, + uri: "/openconfig-acl:cpu/acl-sets/", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "no match found", + }} + + for _, tt := range tests { + deviceObj := ocbinds.Device{} + deviceObj.Acl = &ocbinds.OpenconfigAcl_Acl{} + deviceObj.Acl.AclSets = &ocbinds.OpenconfigAcl_Acl_AclSets{} + deviceObj.Acl.AclSets.NewAclSet("SampleACL", ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4) + + binder := getRequestBinder(&tt.uri, &tt.payload, tt.opcode, &tt.appRootType) + + path, err := binder.getUriPath() + if err != nil { + tmpPath := gnmi.Path{} + binder.pathTmp = &tmpPath + } else { + binder.pathTmp = path + } + + err = binder.validateRequest(&deviceObj) + + if err != nil { + // Negative test case + if strings.Contains(err.Error(), tt.want) == false { + t.Error("Error in validating the object: didn't get the expected error, and the error string is", err) + } + } + } +} + +func TestUnMarshallUri(t *testing.T) { + + tests := []struct { + tid int + uri string + opcode int + payload []byte + appRootType reflect.Type + want string //target object name + }{{ + tid: 1, + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets", + }, { + tid: 2, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/state/description", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "Description", + }, { + tid: 3, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "AclSet", + }, { + tid: 4, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet", + }, { + tid: 5, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=4]", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry", + }, { + //Negative test case + tid: 6, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample]/config", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "failed to create map value for insert, root map[ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_Key]*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet, keys map[name:Sample]: missing type key in map[name:Sample]", + }, { + //Negative test case + tid: 7, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/cnfig", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "rpc error: code = InvalidArgument desc = no match found in *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet", + }, { + //Negative test case + tid: 8, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPVX]/", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "no enum matching with ACL_IPVX: ", + }, { + //Negative test case + tid: 9, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=Sample][type=ACL_IPV4]/state/descriptXX", + opcode: 1, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "rpc error: code = InvalidArgument desc = no match found in *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_State, for path elem: ", *neighborEntry.NeighborAddress) + // } else { + // t.Error("Error in unmarshalling the URI: OpenconfigBgp_Bgp_Neighbors_Neighbor - object casting failed") + // } + } else if tt.tid == 14 { + leafList, ok := (*workObj).([]ocbinds.E_OpenconfigPacketMatchTypes_TCP_FLAGS) + fmt.Println("leaf list target node value is :", leafList) + if ok == true { + if len(leafList) != 1 { + t.Error("Error in unmarshalling the URI with the target node as leaf-list - type is ", reflect.ValueOf(leafList).Type()) + } + } else { + t.Error("Error in unmarshalling the URI with the target node as leaf-list - faile for test case id : ", tt.tid) + } + } else { + _, ok := (*workObj).(ygot.GoStruct) + if ok == false { + // objFieldName, err := getObjectFieldName(&tt.uri, &deviceObj, workObj) + // if err != nil { + // t.Error("Error in unmarshalling the URI: ", err) + // } else if objFieldName != tt.want { + // t.Error("Error in unmarshalling the URI: Invalid target node: ", objFieldName) + // } + } else if tt.tid == 4 { + aclSet, ok := (*workObj).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet) + if ok == true { + if *aclSet.Name != "Sample" && aclSet.Type != ocbinds.OpenconfigAcl_ACL_TYPE_ACL_IPV4 { + t.Error("Error in unmarshalling the URI: Invalid target key leaf node value: ", aclSet.Name) + } + } else { + t.Error("Error in unmarshalling the URI: OpenconfigAcl_Acl_AclSets_AclSet object casting failed") + } + } else if tt.tid == 5 { + aclEntry, ok := (*workObj).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry) + if ok == true { + if *aclEntry.SequenceId != 4 { + t.Error("Error in unmarshalling the URI: Invalid target key leaf node value: ", *aclEntry.SequenceId) + } + } else { + t.Error("Error in unmarshalling the URI: OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry object casting failed") + } + } else if tt.tid == 10 { + cpuObj, ok := (*workObj).(*ocbinds.OpenconfigSystem_System_Cpus_Cpu) + if ok == false { + t.Error("Error in unmarshalling the URI: OpenconfigSystem_System_Cpus_Cpu failed") + } + val := cpuObj.Index.(*ocbinds.OpenconfigSystem_System_Cpus_Cpu_State_Index_Union_Uint32) + cIdx, _ := cpuObj.To_OpenconfigSystem_System_Cpus_Cpu_State_Index_Union(uint32(3)) + val2 := cIdx.(*ocbinds.OpenconfigSystem_System_Cpus_Cpu_State_Index_Union_Uint32) + if *val != *val2 { + t.Error("Error in unmarshalling the URI: OpenconfigSystem_System_Cpus_Cpu failed") + } + } else if tt.tid == 11 { + cpuObj, ok := (*workObj).(*ocbinds.OpenconfigSystem_System_Cpus_Cpu) + if ok == false { + t.Error("Error in unmarshalling the URI: OpenconfigSystem_System_Cpus_Cpu failed") + } + val := cpuObj.Index.(*ocbinds.OpenconfigSystem_System_Cpus_Cpu_State_Index_Union_E_OpenconfigSystem_System_Cpus_Cpu_State_Index) + cIdx, err := cpuObj.To_OpenconfigSystem_System_Cpus_Cpu_State_Index_Union(ocbinds.E_OpenconfigSystem_System_Cpus_Cpu_State_Index(1)) + if err != nil { + t.Errorf("Error in unmarshalling the URI: OpenconfigSystem_System_Cpus_Cpu failed %s", fmt.Sprint(err)) + } + val2 := cIdx.(*ocbinds.OpenconfigSystem_System_Cpus_Cpu_State_Index_Union_E_OpenconfigSystem_System_Cpus_Cpu_State_Index) + if *val != *val2 { + t.Error("Error in unmarshalling the URI: OpenconfigSystem_System_Cpus_Cpu failed") + } + } else if reflect.TypeOf(*workObj).Elem().Name() != tt.want { + t.Error("Error in unmarshalling the URI: Invalid target node: ", reflect.TypeOf(*workObj).Elem().Name()) + } + } + } +} + +func TestUnMarshallPayload(t *testing.T) { + tests := []struct { + tid int + objIntf interface{} + uri string + opcode int + payload []byte + want string //target object name + }{{ + tid: 1, + objIntf: "TestObj", + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 2, + payload: []byte{}, + want: "Error in casting the target object", + }, { + tid: 2, + objIntf: ocbinds.OpenconfigAcl_Acl{}, + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 3, + payload: []byte{}, + want: "Request payload is empty", + }} + + for _, tt := range tests { + objType := reflect.TypeOf(tt.objIntf) + reqBinder := getRequestBinder(&tt.uri, &tt.payload, tt.opcode, &objType) + var deviceObj ocbinds.Device = ocbinds.Device{} + var workObj *interface{} + var err error + workObj, err = reqBinder.unMarshallUri(&deviceObj) + if err != nil { + t.Error(err) + } + + if tt.tid == 1 { + workObj = &tt.objIntf + } + + err = reqBinder.unMarshallPayload(workObj) + if err != nil { + if strings.Contains(err.Error(), tt.want) == false { + t.Error("Negative test case failed: ", err) + } + } + } +} + +func TestGetUriPath(t *testing.T) { + + tests := []struct { + tid int + uri string + opcode int + payload []byte + appRootType reflect.Type + want string //target object name + }{{ + tid: 1, + uri: "////", + opcode: 2, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "error formatting path", + }} + + for _, tt := range tests { + reqBinder := getRequestBinder(&tt.uri, &tt.payload, tt.opcode, &tt.appRootType) + _, err := reqBinder.getUriPath() + if err != nil { + if strings.Contains(err.Error(), tt.want) == false { + t.Error("Negative test case failed: ", err) + } + } + } +} + +func TestUnMarshall(t *testing.T) { + + tests := []struct { + tid int + uri string + opcode int + payload []byte + appRootType reflect.Type + want string //target object name + }{{ + tid: 1, + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 2, + payload: []byte("{ \"acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets", + }, { + //Negative test case + tid: 2, + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 2, + payload: []byte("{ \"acl-set\": [ { \"nname\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "parent container acl-set (type *ocbinds.OpenconfigAcl_Acl_AclSets_AclSet): JSON contains unexpected field nname", + }, { + //Negative test case + tid: 3, + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 2, + payload: []byte("{ \"acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV7\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "ACL_IPV7 is not a valid value for enum field Type, type ocbinds.E_OpenconfigAcl_ACL_TYPE", + }, { + tid: 4, + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/", + opcode: 2, + payload: []byte("{ \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": 1, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL1 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] } } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet", + }, { + tid: 5, //PUT + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/acl-entries", + opcode: 3, + payload: []byte("{ \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": 1, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL1 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] } } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet", + }, { + tid: 6, //PATCH + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/acl-entries", + opcode: 4, + payload: []byte("{ \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": 1, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL1 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] } } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet", + }, { + tid: 7, //DELETE + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/", + opcode: 5, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "OpenconfigAcl_Acl_AclSets_AclSet", + }, { + tid: 8, //GET on leaf node + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=4]/ipv4/config/source-address", + opcode: 5, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "SourceAddress", + }, { + tid: 9, //negative test case + uri: "/openconfig-acl:acl/", + opcode: 3, + payload: []byte{}, + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "Request payload is empty", + }, { + tid: 10, //GET - negative test case - incorrect opcode + uri: "/openconfig-acl:acl/acl-sets/", + opcode: 7, + payload: []byte("{ \"acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "Unknown HTTP METHOD in the request", + }, { + tid: 11, // negative + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set/", + opcode: 2, + payload: []byte("{ \"acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "URI doesn't have keys in the request", + }, { + tid: 12, // negative + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL1][type=ACL_IPV4]/", + opcode: 2, + payload: []byte("{ \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": abc, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL1 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] } } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "invalid character", + }, { + tid: 13, // negative + uri: "/openconfig-acl:acl/acl-sets/acl-set[name=MyACL1]", + opcode: 1, + payload: []byte("{ \"acl-entries\": { \"acl-entry\": [ { \"sequence-id\": abc, \"config\": { \"sequence-id\": 1, \"description\": \"Description for MyACL1 Rule Seq 1\" }, \"ipv4\": { \"config\": { \"source-address\": \"11.1.1.1/32\", \"destination-address\": \"21.1.1.1/32\", \"dscp\": 1, \"protocol\": \"IP_TCP\" } }, \"transport\": { \"config\": { \"source-port\": 101, \"destination-port\": 201 } }, \"actions\": { \"config\": { \"forwarding-action\": \"ACCEPT\" } } } ] } } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "failed to create map value for insert", + // }, { + // tid: 14, // PATCH - test bgp neighbor bind with update + // uri: "/openconfig-bgp:bgp/neighbors/neighbor[neighbor-address=30.0.0.1]/ebgp-multihop/config/multihop-ttl", + // opcode: 4, + // payload: []byte("{\"openconfig-bgp:multihop-ttl\": 3}"), + // appRootType: reflect.TypeOf(ocbinds.OpenconfigBgp_Bgp{}), + // want: "15", + }, { + tid: 15, // PATCH - from the base node acl + uri: "/openconfig-acl:acl/", + opcode: 4, + payload: []byte("{ \"openconfig-acl:acl\": { \"acl-sets\": { \"acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } } }"), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "XXX", + }, { + tid: 16, // PATCH - from the base node acl + uri: "/openconfig-acl:acl/acl-sets/acl-set", + opcode: 4, + payload: []byte("{ \"acl-set\": [ { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL3\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL3\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "XXX", + }, { + tid: 17, // PATCH - from the base node acl + uri: "/openconfig-acl:acl/acl-sets/openconfig-acl:acl-set[name=MyACL333][type=ACL_IPV4]/", + opcode: 4, + payload: []byte("{ \"acl-set\": [ { \"name\": \"MyACL333\", \"type\": \"ACL_IPV4\", \"config\": { \"name\": \"MyACL333\", \"type\": \"ACL_IPV4\", \"description\": \"Description for MyACL333\" } } ] } "), + appRootType: reflect.TypeOf(ocbinds.OpenconfigAcl_Acl{}), + want: "XXX", + }} + + for _, tt := range tests { + _, workObj, err := getRequestBinder(&tt.uri, &tt.payload, tt.opcode, &tt.appRootType).unMarshall() + + if err != nil { + if strings.Contains(err.Error(), tt.want) == false { + t.Errorf("TestUnMarshall: Testcase id: %d Error in unmarshalling the payload: didn't get the expected error, and the error string is %s ", tt.tid, err) + } + } else { + if tt.tid == 17 { + fmt.Println("TestUnMarshall: Testcase 17 passed - workObj: ", *workObj) + if aclSet, ok := (*workObj).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet); ok { + fmt.Printf("TestUnMarshall: Testcase 17 passed - Acl Name: %s, and Acl Type: %d\n", *aclSet.Name, aclSet.Type) + } else { + t.Error("TestUnMarshall: Testcase 17 - Error in unmarshalling the payload: OpenconfigAcl_Acl_AclSets") + } + } else if tt.tid == 16 { + if reflect.ValueOf(*workObj).Kind().String() == "map" { + fmt.Println("TestUnMarshall: Testcase 16 passed - workobj: ", *workObj) + } else { + t.Error("TestUnMarshall: Testcase 16 - Error in unmarshalling the payload: OpenconfigAcl_AclSet") + } + } else if tt.tid == 15 { + aclObj, ok := (*workObj).(*ocbinds.OpenconfigAcl_Acl) + if ok == true { + fmt.Println("TestUnMarshall: Testcase 15 passed - ", aclObj) + } else { + t.Error("TestUnMarshall: Testcase 15 - Error in unmarshalling the payload: OpenconfigAcl_Acl") + } + } else if tt.tid == 14 { + ttlVal, ok := (*workObj).(*uint8) + if ok == false || *ttlVal != 3 { + t.Error("TestUnMarshall: Testcase 14 - Error in unmarshalling the payload: OpenconfigBgp_Bgp_Neighbors_Neighbor_EbgpMultihop_Config_multihop-ttl failed") + } else { + fmt.Println("TestUnMarshall: Testcase 14 passed - ptach method: => multihop-ttl value ==> ", *ttlVal) + } + } else if tt.tid == 4 { + aclSet, ok := (*workObj).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet) + if ok == true { + if aclSet.AclEntries.AclEntry[1] != nil && *aclSet.AclEntries.AclEntry[1].SequenceId == 1 { + _, err = aclSet.AclEntries.AclEntry[1].Transport.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Transport_Config_DestinationPort_Union(uint16(201)) + if err != nil { + t.Error("Error in unmarshalling the payload: ", err) + } + } else { + t.Error("Error in unmarshalling the payload") + } + } else { + t.Error("Error in unmarshalling the payload: OpenconfigAcl_Acl_AclSets_AclSet object casting failed") + } + } else if tt.tid == 5 || tt.tid == 6 { + aclEntries, ok := (*workObj).(*ocbinds.OpenconfigAcl_Acl_AclSets_AclSet_AclEntries) + if ok == true { + if aclEntries.AclEntry[1] != nil && *aclEntries.AclEntry[1].SequenceId == 1 { + _, err = aclEntries.AclEntry[1].Ipv4.Config.To_OpenconfigAcl_Acl_AclSets_AclSet_AclEntries_AclEntry_Ipv4_Config_Protocol_Union(ocbinds.OpenconfigPacketMatchTypes_IP_PROTOCOL_IP_TCP) + if err != nil { + t.Error("Error in unmarshalling the payload: ", err) + } + } else { + t.Error("Error in unmarshalling the payload") + } + } else { + t.Error("Error in unmarshalling the payload: OpenconfigAcl_Acl_AclSets_AclSet object casting failed") + } + } else { + _, ok := (*workObj).(ygot.GoStruct) + if ok == false { + // objFieldName, err := getObjectFieldName(&tt.uri, (*rootObj).(*ocbinds.Device), workObj) + // if err != nil { + // t.Error("Error in unmarshalling the URI: ", err) + // } else if objFieldName != tt.want { + // t.Error("Error in unmarshalling the payload: Invalid target node: ", objFieldName) + // } + } else if reflect.TypeOf(*workObj).Elem().Name() != tt.want { + t.Error("Error in unmarshalling the payload: Invalid target node: ", reflect.TypeOf(*workObj).Elem().Name()) + } + } + } + } +} diff --git a/src/translib/stp_app.go b/src/translib/stp_app.go new file mode 100644 index 0000000000..20a6f1b294 --- /dev/null +++ b/src/translib/stp_app.go @@ -0,0 +1,2391 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "errors" + "reflect" + "strconv" + "strings" + "translib/db" + "translib/ocbinds" + "translib/tlerr" + + "github.com/facette/natsort" + log "github.com/golang/glog" + "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygot" +) + +const ( + STP_GLOBAL_TABLE = "STP" + STP_VLAN_TABLE = "STP_VLAN" + STP_VLAN_INTF_TABLE = "STP_VLAN_INTF" + STP_INTF_TABLE = "STP_INTF" + STP_VLAN_OPER_TABLE = "_STP_VLAN_TABLE" + STP_VLAN_INTF_OPER_TABLE = "_STP_VLAN_INTF_TABLE" + STP_INTF_OPER_TABLE = "_STP_INTF_TABLE" + STP_MODE = "mode" + OC_STP_APP_MODULE_NAME = "/openconfig-spanning-tree:stp" + OC_STP_YANG_PATH_PREFIX = "/device/stp" + PVST_MAX_INSTANCES = 255 + + STP_DEFAULT_ROOT_GUARD_TIMEOUT = "30" + STP_DEFAULT_FORWARD_DELAY = "15" + STP_DEFAULT_HELLO_INTERVAL = "2" + STP_DEFAULT_MAX_AGE = "20" + STP_DEFAULT_BRIDGE_PRIORITY = "32768" + STP_DEFAULT_BPDU_FILTER = "false" +) + +type StpApp struct { + pathInfo *PathInfo + ygotRoot *ygot.GoStruct + ygotTarget *interface{} + + globalTable *db.TableSpec + vlanTable *db.TableSpec + vlanIntfTable *db.TableSpec + interfaceTable *db.TableSpec + + vlanOperTable *db.TableSpec + vlanIntfOperTable *db.TableSpec + intfOperTable *db.TableSpec + + globalInfo db.Value + vlanTableMap map[string]db.Value + vlanIntfTableMap map[string]map[string]db.Value + intfTableMap map[string]db.Value + + vlanOperTableMap map[string]db.Value + vlanIntfOperTableMap map[string]map[string]db.Value + intfOperTableMap map[string]db.Value + + appDB *db.DB +} + +func init() { + err := register("/openconfig-spanning-tree:stp", + &appInfo{appType: reflect.TypeOf(StpApp{}), + ygotRootType: reflect.TypeOf(ocbinds.OpenconfigSpanningTree_Stp{}), + isNative: false, + tablesToWatch: []*db.TableSpec{&db.TableSpec{Name: STP_GLOBAL_TABLE}, &db.TableSpec{Name: STP_VLAN_TABLE}, &db.TableSpec{Name: STP_VLAN_INTF_TABLE}, &db.TableSpec{Name: STP_INTF_TABLE}}}) + + if err != nil { + log.Fatal("Register STP app module with App Interface failed with error=", err) + } + + err = addModel(&ModelData{Name: "openconfig-spanning-tree", Org: "OpenConfig working group", Ver: "0.3.0"}) + if err != nil { + log.Fatal("Adding model data to appinterface failed with error=", err) + } +} + +func (app *StpApp) initialize(data appData) { + log.Info("initialize:stp:path =", data.path) + app.pathInfo = NewPathInfo(data.path) + app.ygotRoot = data.ygotRoot + app.ygotTarget = data.ygotTarget + + app.globalTable = &db.TableSpec{Name: STP_GLOBAL_TABLE} + app.vlanTable = &db.TableSpec{Name: STP_VLAN_TABLE} + app.vlanIntfTable = &db.TableSpec{Name: STP_VLAN_INTF_TABLE} + app.interfaceTable = &db.TableSpec{Name: STP_INTF_TABLE} + + app.vlanOperTable = &db.TableSpec{Name: STP_VLAN_OPER_TABLE} + app.vlanIntfOperTable = &db.TableSpec{Name: STP_VLAN_INTF_OPER_TABLE} + app.intfOperTable = &db.TableSpec{Name: STP_INTF_OPER_TABLE} + + app.globalInfo = db.Value{Field: map[string]string{}} + app.vlanTableMap = make(map[string]db.Value) + app.intfTableMap = make(map[string]db.Value) + app.vlanIntfTableMap = make(map[string]map[string]db.Value) + + app.vlanOperTableMap = make(map[string]db.Value) + app.vlanIntfOperTableMap = make(map[string]map[string]db.Value) + app.intfOperTableMap = make(map[string]db.Value) +} + +func (app *StpApp) getAppRootObject() *ocbinds.OpenconfigSpanningTree_Stp { + deviceObj := (*app.ygotRoot).(*ocbinds.Device) + return deviceObj.Stp +} + +func (app *StpApp) translateCreate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCreate:stp:path =", app.pathInfo.Template) + + keys, err = app.translateCRUCommon(d, CREATE) + return keys, err +} + +func (app *StpApp) translateUpdate(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateUpdate:stp:path =", app.pathInfo.Template) + + keys, err = app.translateCRUCommon(d, UPDATE) + return keys, err +} + +func (app *StpApp) translateReplace(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateReplace:stp:path =", app.pathInfo.Template) + + keys, err = app.translateCRUCommon(d, REPLACE) + return keys, err +} + +func (app *StpApp) translateDelete(d *db.DB) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateDelete:stp:path =", app.pathInfo.Template) + + return keys, err +} + +func (app *StpApp) translateGet(dbs [db.MaxDB]*db.DB) error { + var err error + log.Info("translateGet:stp:path =", app.pathInfo.Template) + return err +} + +func (app *StpApp) translateAction(dbs [db.MaxDB]*db.DB) error { + err := errors.New("Not supported") + return err +} + +func (app *StpApp) translateSubscribe(dbs [db.MaxDB]*db.DB, path string) (*notificationOpts, *notificationInfo, error) { + var err error + return nil, nil, err +} + +func (app *StpApp) processCreate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, CREATE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *StpApp) processUpdate(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, UPDATE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *StpApp) processReplace(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, REPLACE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *StpApp) processDelete(d *db.DB) (SetResponse, error) { + var err error + var resp SetResponse + + if err = app.processCommon(d, DELETE); err != nil { + log.Error(err) + resp = SetResponse{ErrSrc: AppErr} + } + return resp, err +} + +func (app *StpApp) processGet(dbs [db.MaxDB]*db.DB) (GetResponse, error) { + var err error + var payload []byte + + app.appDB = dbs[db.ApplDB] + + configDb := dbs[db.ConfigDB] + err = app.processCommon(configDb, GET) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + payload, err = generateGetResponsePayload(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device), app.ygotTarget) + if err != nil { + return GetResponse{Payload: payload, ErrSrc: AppErr}, err + } + + return GetResponse{Payload: payload}, err +} + +func (app *StpApp) processAction(dbs [db.MaxDB]*db.DB) (ActionResponse, error) { + var resp ActionResponse + err := errors.New("Not implemented") + + return resp, err +} + +func (app *StpApp) translateCRUCommon(d *db.DB, opcode int) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + log.Info("translateCRUCommon:STP:path =", app.pathInfo.Template) + + err = app.convertOCStpGlobalConfToInternal(opcode) + if err != nil { + return keys, err + } + err = app.convertOCPvstToInternal(opcode) + if err != nil { + return keys, err + } + err = app.convertOCRpvstConfToInternal(opcode) + if err != nil { + return keys, err + } + app.convertOCStpInterfacesToInternal() + + return keys, err +} + +func (app *StpApp) processCommon(d *db.DB, opcode int) error { + var err error + var topmostPath bool = false + stp := app.getAppRootObject() + + log.Infof("processCommon--Path Received: %s", app.pathInfo.Template) + targetType := reflect.TypeOf(*app.ygotTarget) + if !util.IsValueScalar(reflect.ValueOf(*app.ygotTarget)) && util.IsValuePtr(reflect.ValueOf(*app.ygotTarget)) { + log.Infof("processCommon: Target object is a <%s> of Type: %s", targetType.Kind().String(), targetType.Elem().Name()) + if targetType.Elem().Name() == "OpenconfigSpanningTree_Stp" { + topmostPath = true + } + } + + targetUriPath, _ := getYangPathFromUri(app.pathInfo.Path) + log.Infof("processCommon -- isTopmostPath: %t and Uri: %s", topmostPath, targetUriPath) + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/global") { + switch opcode { + case CREATE: + err = app.enableStpMode(d) + case REPLACE, UPDATE: + mode, _ := app.getStpModeFromConfigDB(d) + if *app.ygotTarget == stp.Global || *app.ygotTarget == stp.Global.Config || targetUriPath == "/openconfig-spanning-tree:stp/global/config/enabled-protocol" { + if len(mode) == 0 { + err = app.enableStpMode(d) + } else { + if mode != app.convertOCStpModeToInternal(stp.Global.Config.EnabledProtocol[0]) { + return tlerr.InvalidArgs("STP mode is configured as %s", mode) + } else { + app.handleStpGlobalFieldsUpdation(d, opcode) + } + } + } else { + app.handleStpGlobalFieldsUpdation(d, opcode) + } + case DELETE: + if *app.ygotTarget == stp.Global || *app.ygotTarget == stp.Global.Config || targetUriPath == "/openconfig-spanning-tree:stp/global/config/enabled-protocol" { + if app.pathInfo.Template == "/openconfig-spanning-tree:stp/global/config/enabled-protocol{}" { + mode, _ := app.getStpModeFromConfigDB(d) + if mode != app.convertOCStpModeToInternal(stp.Global.Config.EnabledProtocol[0]) { + return tlerr.InvalidArgs("STP mode is configured as %s", mode) + } + } + err = app.disableStpMode(d) + } else { + err = app.handleStpGlobalFieldsDeletion(d) + } + case GET: + err = app.convertDBStpGlobalConfigToInternal(d) + if err != nil { + return err + } + ygot.BuildEmptyTree(stp) + app.convertInternalToOCStpGlobalConfig(stp.Global) + } + } else if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst") { + mode, _ := app.getStpModeFromConfigDB(d) + if mode != "pvst" { + return tlerr.InvalidArgs("STP mode is configured as %s", mode) + } + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlan{}") { + for vlanId, _ := range stp.Pvst.Vlan { + pvstVlan := stp.Pvst.Vlan[vlanId] + vlanName := "Vlan" + strconv.Itoa(int(vlanId)) + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlan{}/interfaces/interface{}") { + // Subtree of one interface under a vlan + for intfId, _ := range pvstVlan.Interfaces.Interface { + pvstVlanIntf := pvstVlan.Interfaces.Interface[intfId] + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + err = app.handleRpvstCRUDOperationsAtVlanInterfaceLevel(d, opcode, vlanName, intfId, pvstVlan, pvstVlanIntf) + if err != nil { + return err + } + case GET: + err = app.convertDBRpvstVlanInterfaceToInternal(d, vlanName, intfId, asKey(vlanName, intfId), true) + if err != nil { + return err + } + ygot.BuildEmptyTree(pvstVlanIntf) + app.convertInternalToOCPvstVlanInterface(vlanName, intfId, pvstVlan, pvstVlanIntf) + // populate operational data + app.convertOperInternalToOCVlanInterface(vlanName, intfId, pvstVlan, pvstVlanIntf) + } + } + } else { + isInterfacesSubtree := isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/openconfig-spanning-tree-ext:pvst/vlan{}/interfaces") + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + err = app.handleRpvstCRUDOperationsAtVlanLevel(d, opcode, vlanName, isInterfacesSubtree, stp.Pvst, pvstVlan) + if err != nil { + return err + } + case GET: + err = app.convertDBRpvstVlanConfigToInternal(d, asKey(vlanName)) + if err != nil { + return err + } + ygot.BuildEmptyTree(pvstVlan) + app.convertInternalToOCPvstVlan(vlanName, stp.Pvst, pvstVlan) + } + } + } + } else { + // Handle top PVST + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + log.Infof("Implementation in progress for URL: %s", app.pathInfo.Template) + return tlerr.NotSupported("Implementation in progress") + case GET: + ygot.BuildEmptyTree(stp.Pvst) + err = app.convertDBRpvstVlanConfigToInternal(d, db.Key{}) + if err != nil { + return err + } + app.convertInternalToOCPvstVlan("", stp.Pvst, nil) + } + } + } else if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/rapid-pvst") { + mode, _ := app.getStpModeFromConfigDB(d) + if mode != "rpvst" { + return tlerr.InvalidArgs("STP mode is configured as %s", mode) + } + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/rapid-pvst/vlan{}") { + for vlanId, _ := range stp.RapidPvst.Vlan { + rpvstVlanConf := stp.RapidPvst.Vlan[vlanId] + vlanName := "Vlan" + strconv.Itoa(int(vlanId)) + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/rapid-pvst/vlan{}/interfaces/interface{}") { + // Subtree of one interface under a vlan + for intfId, _ := range rpvstVlanConf.Interfaces.Interface { + rpvstVlanIntfConf := rpvstVlanConf.Interfaces.Interface[intfId] + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + err = app.handleRpvstCRUDOperationsAtVlanInterfaceLevel(d, opcode, vlanName, intfId, rpvstVlanConf, rpvstVlanIntfConf) + if err != nil { + return err + } + case GET: + err = app.convertDBRpvstVlanInterfaceToInternal(d, vlanName, intfId, asKey(vlanName, intfId), true) + if err != nil { + return err + } + ygot.BuildEmptyTree(rpvstVlanIntfConf) + app.convertInternalToOCRpvstVlanInterface(vlanName, intfId, rpvstVlanConf, rpvstVlanIntfConf) + // populate operational data + app.convertOperInternalToOCVlanInterface(vlanName, intfId, rpvstVlanConf, rpvstVlanIntfConf) + } + } + } else { + isInterfacesSubtree := isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/rapid-pvst/vlan{}/interfaces") + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + err = app.handleRpvstCRUDOperationsAtVlanLevel(d, opcode, vlanName, isInterfacesSubtree, stp.RapidPvst, rpvstVlanConf) + if err != nil { + return err + } + case GET: + err = app.convertDBRpvstVlanConfigToInternal(d, asKey(vlanName)) + if err != nil { + return err + } + ygot.BuildEmptyTree(rpvstVlanConf) + app.convertInternalToOCRpvstVlanConfig(vlanName, stp.RapidPvst, rpvstVlanConf) + } + } + } + } else { + // Handle both rapid-pvst and rapid-pvst/vlan + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + log.Infof("Implementation in progress for URL: %s", app.pathInfo.Template) + return tlerr.NotSupported("Implementation in progress") + case GET: + ygot.BuildEmptyTree(stp.RapidPvst) + err = app.convertDBRpvstVlanConfigToInternal(d, db.Key{}) + if err != nil { + return err + } + app.convertInternalToOCRpvstVlanConfig("", stp.RapidPvst, nil) + } + } + } else if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/mstp") { + mode, _ := app.getStpModeFromConfigDB(d) + if mode != "mstp" { + return tlerr.InvalidArgs("STP mode is configured as %s", mode) + } + } else if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/interfaces") { + if isSubtreeRequest(app.pathInfo.Template, "/openconfig-spanning-tree:stp/interfaces/interface{}") { + for intfId, _ := range stp.Interfaces.Interface { + intfData := stp.Interfaces.Interface[intfId] + switch opcode { + case CREATE: + err = app.setStpInterfacesDataInDB(d, true) + case REPLACE: + if *app.ygotTarget == intfData { + err = d.DeleteEntry(app.interfaceTable, asKey(intfId)) + if err != nil { + return err + } + err = app.setStpInterfacesDataInDB(d, true) + } else { + err = app.setStpInterfacesDataInDB(d, false) + } + case UPDATE: + err = app.setStpInterfacesDataInDB(d, false) + case DELETE: + if *app.ygotTarget == intfData { + err = d.DeleteEntry(app.interfaceTable, asKey(intfId)) + } else { + err = app.handleInterfacesFieldsDeletion(d, intfId) + } + case GET: + err = app.convertDBStpInterfacesToInternal(d, asKey(intfId)) + if err != nil { + return err + } + ygot.BuildEmptyTree(intfData) + app.convertInternalToOCStpInterfaces(intfId, stp.Interfaces, intfData) + } + } + } else { + switch opcode { + case CREATE, REPLACE, UPDATE, DELETE: + log.Infof("Implementation in progress for URL: %s", app.pathInfo.Template) + return tlerr.NotSupported("Implementation in progress") + case GET: + ygot.BuildEmptyTree(stp.Interfaces) + err = app.convertDBStpInterfacesToInternal(d, db.Key{}) + if err != nil { + return err + } + app.convertInternalToOCStpInterfaces("", stp.Interfaces, nil) + } + } + } else if topmostPath { + switch opcode { + case CREATE: + err = app.enableStpMode(d) + if err != nil { + return err + } + err = app.setRpvstVlanDataInDB(d, true) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, true) + if err != nil { + return err + } + err = app.setStpInterfacesDataInDB(d, true) + case REPLACE: + err = app.disableStpMode(d) + if err != nil { + return err + } + err = app.enableStpMode(d) + if err != nil { + return err + } + err = app.setRpvstVlanDataInDB(d, true) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, true) + if err != nil { + return err + } + err = app.setStpInterfacesDataInDB(d, true) + case DELETE: + err = app.disableStpMode(d) + case GET: + ygot.BuildEmptyTree(stp) + ////////////////////// + err = app.convertDBStpGlobalConfigToInternal(d) + if err != nil { + return err + } + app.convertInternalToOCStpGlobalConfig(stp.Global) + + ////////////////////// + err = app.convertDBRpvstVlanConfigToInternal(d, db.Key{}) + if err != nil { + return err + } + stpMode := (&app.globalInfo).Get(STP_MODE) + switch stpMode { + case "pvst": + ygot.BuildEmptyTree(stp.Pvst) + app.convertInternalToOCPvstVlan("", stp.Pvst, nil) + case "rpvst": + ygot.BuildEmptyTree(stp.RapidPvst) + app.convertInternalToOCRpvstVlanConfig("", stp.RapidPvst, nil) + case "mstp": + } + ////////////////////// + ygot.BuildEmptyTree(stp.Interfaces) + err = app.convertDBStpInterfacesToInternal(d, db.Key{}) + if err != nil { + return err + } + app.convertInternalToOCStpInterfaces("", stp.Interfaces, nil) + } + } + + return err +} + +func (app *StpApp) processCommonRpvstVlanToplevelPath(d *db.DB, stp *ocbinds.OpenconfigSpanningTree_Stp, opcode int) error { + var err error + + switch opcode { + case CREATE: + case REPLACE: + case UPDATE: + case DELETE: + case GET: + } + + return err +} + +func (app *StpApp) handleRpvstCRUDOperationsAtVlanLevel(d *db.DB, opcode int, vlanName string, isInterfacesSubtree bool, mode interface{}, vlan interface{}) error { + var err error + + log.Infof("handleRpvstCRUDOperationsAtVlanLevel --> Perform CRUD for %s", reflect.TypeOf(vlan).Elem().Name()) + switch opcode { + case CREATE: + if *app.ygotTarget == vlan { + err = app.setRpvstVlanDataInDB(d, true) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, true) + } else if isInterfacesSubtree { + err = app.setRpvstVlanInterfaceDataInDB(d, true) + } else { + err = d.SetEntry(app.vlanTable, asKey(vlanName), app.vlanTableMap[vlanName]) + } + case REPLACE: + if *app.ygotTarget == vlan { + err = d.DeleteKeys(app.vlanIntfTable, asKey(vlanName+TABLE_SEPARATOR+"*")) + if err != nil { + return err + } + err = d.DeleteEntry(app.vlanTable, asKey(vlanName)) + if err != nil { + return err + } + err = app.setRpvstVlanDataInDB(d, true) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, true) + } else if isInterfacesSubtree { + err = d.DeleteKeys(app.vlanIntfTable, asKey(vlanName+TABLE_SEPARATOR+"*")) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, true) + } else { + err = d.SetEntry(app.vlanTable, asKey(vlanName), app.vlanTableMap[vlanName]) + } + case UPDATE: + if *app.ygotTarget == vlan { + err = app.setRpvstVlanDataInDB(d, false) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, false) + } else if isInterfacesSubtree { + err = app.setRpvstVlanInterfaceDataInDB(d, false) + } else { + err = d.ModEntry(app.vlanTable, asKey(vlanName), app.vlanTableMap[vlanName]) + } + case DELETE: + if *app.ygotTarget == vlan { + err = d.DeleteKeys(app.vlanIntfTable, asKey(vlanName+TABLE_SEPARATOR+"*")) + if err != nil { + return err + } + err = d.DeleteEntry(app.vlanTable, asKey(vlanName)) + } else if isInterfacesSubtree { + err = d.DeleteKeys(app.vlanIntfTable, asKey(vlanName+TABLE_SEPARATOR+"*")) + } else { + err = app.handleVlanFieldsDeletion(d, vlanName) + } + } + + return err +} + +func (app *StpApp) handleRpvstCRUDOperationsAtVlanInterfaceLevel(d *db.DB, opcode int, vlanName string, intfId string, vlan interface{}, vlanIntf interface{}) error { + var err error + + log.Infof("handleRpvstCRUDOperationsAtVlanInterfaceLevel --> Perform CRUD for %s", reflect.TypeOf(vlanIntf).Elem().Name()) + switch opcode { + case CREATE: + if *app.ygotTarget == vlanIntf { + err = app.setRpvstVlanInterfaceDataInDB(d, true) + } else { + err = app.setRpvstVlanInterfaceDataInDB(d, false) + } + case REPLACE: + if *app.ygotTarget == vlanIntf { + err = d.DeleteEntry(app.vlanIntfTable, asKey(vlanName, intfId)) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, true) + } else { + err = app.handleVlanInterfaceFieldsDeletion(d, vlanName, intfId) + if err != nil { + return err + } + err = app.setRpvstVlanInterfaceDataInDB(d, false) + } + case UPDATE: + err = app.setRpvstVlanInterfaceDataInDB(d, false) + case DELETE: + if *app.ygotTarget == vlanIntf { + err = d.DeleteEntry(app.vlanIntfTable, asKey(vlanName, intfId)) + } else { + err = app.handleVlanInterfaceFieldsDeletion(d, vlanName, intfId) + } + } + + return err +} + +///////////////// STP GLOBAL ////////////////////// +func (app *StpApp) setStpGlobalConfigInDB(d *db.DB) error { + var err error + + err = d.CreateEntry(app.globalTable, asKey("GLOBAL"), app.globalInfo) + + return err +} + +func (app *StpApp) convertOCStpGlobalConfToInternal(opcode int) error { + var err error + stp := app.getAppRootObject() + setDefaultFlag := (opcode == CREATE || opcode == REPLACE) + if stp != nil { + if stp.Global != nil && stp.Global.Config != nil { + if stp.Global.Config.BridgePriority != nil { + priorityVal := int(*stp.Global.Config.BridgePriority) + if (priorityVal % 4096) != 0 { + return tlerr.InvalidArgs("Priority value should be multiple of 4096") + } + (&app.globalInfo).Set("priority", strconv.Itoa(priorityVal)) + } else if setDefaultFlag { + (&app.globalInfo).Set("priority", STP_DEFAULT_BRIDGE_PRIORITY) + } + if stp.Global.Config.ForwardingDelay != nil { + (&app.globalInfo).Set("forward_delay", strconv.Itoa(int(*stp.Global.Config.ForwardingDelay))) + } else if setDefaultFlag { + (&app.globalInfo).Set("forward_delay", STP_DEFAULT_FORWARD_DELAY) + } + if stp.Global.Config.HelloTime != nil { + (&app.globalInfo).Set("hello_time", strconv.Itoa(int(*stp.Global.Config.HelloTime))) + } else if setDefaultFlag { + (&app.globalInfo).Set("hello_time", STP_DEFAULT_HELLO_INTERVAL) + } + if stp.Global.Config.MaxAge != nil { + (&app.globalInfo).Set("max_age", strconv.Itoa(int(*stp.Global.Config.MaxAge))) + } else if setDefaultFlag { + (&app.globalInfo).Set("max_age", STP_DEFAULT_MAX_AGE) + } + if stp.Global.Config.RootguardTimeout != nil { + (&app.globalInfo).Set("rootguard_timeout", strconv.Itoa(int(*stp.Global.Config.RootguardTimeout))) + } else if setDefaultFlag { + (&app.globalInfo).Set("rootguard_timeout", STP_DEFAULT_ROOT_GUARD_TIMEOUT) + } + if stp.Global.Config.BpduFilter != nil { + if *stp.Global.Config.BpduFilter == true { + (&app.globalInfo).Set("bpdu_filter", "true") + } else { + (&app.globalInfo).Set("bpdu_filter", "false") + } + } else if setDefaultFlag { + (&app.globalInfo).Set("bpdu_filter", STP_DEFAULT_BPDU_FILTER) + } + + if len(stp.Global.Config.EnabledProtocol) > 0 { + mode := app.convertOCStpModeToInternal(stp.Global.Config.EnabledProtocol[0]) + if len(mode) > 0 { + (&app.globalInfo).Set(STP_MODE, mode) + } + } + + log.Infof("convertOCStpGlobalConfToInternal -- Internal Stp global config: %v", app.globalInfo) + } + } + return err +} + +func (app *StpApp) convertDBStpGlobalConfigToInternal(d *db.DB) error { + var err error + + app.globalInfo, err = d.GetEntry(app.globalTable, asKey("GLOBAL")) + if err != nil { + return err + } + return err +} + +func (app *StpApp) convertInternalToOCStpGlobalConfig(stpGlobal *ocbinds.OpenconfigSpanningTree_Stp_Global) { + if stpGlobal != nil { + var priority uint32 + var forDelay, helloTime, maxAge uint8 + var rootGTimeout uint16 + var bpduFilter bool + ygot.BuildEmptyTree(stpGlobal) + + if stpGlobal.Config != nil { + stpGlobal.Config.EnabledProtocol = app.convertInternalStpModeToOC((&app.globalInfo).Get(STP_MODE)) + + var num uint64 + num, _ = strconv.ParseUint((&app.globalInfo).Get("priority"), 10, 32) + priority = uint32(num) + stpGlobal.Config.BridgePriority = &priority + + num, _ = strconv.ParseUint((&app.globalInfo).Get("forward_delay"), 10, 8) + forDelay = uint8(num) + stpGlobal.Config.ForwardingDelay = &forDelay + + num, _ = strconv.ParseUint((&app.globalInfo).Get("hello_time"), 10, 8) + helloTime = uint8(num) + stpGlobal.Config.HelloTime = &helloTime + + num, _ = strconv.ParseUint((&app.globalInfo).Get("max_age"), 10, 8) + maxAge = uint8(num) + stpGlobal.Config.MaxAge = &maxAge + + num, _ = strconv.ParseUint((&app.globalInfo).Get("rootguard_timeout"), 10, 16) + rootGTimeout = uint16(num) + stpGlobal.Config.RootguardTimeout = &rootGTimeout + + bpduFilter, _ = strconv.ParseBool((&app.globalInfo).Get("bpdu_filter")) + stpGlobal.Config.BpduFilter = &bpduFilter + } + if stpGlobal.State != nil { + stpGlobal.State.EnabledProtocol = app.convertInternalStpModeToOC((&app.globalInfo).Get(STP_MODE)) + stpGlobal.State.BridgePriority = &priority + stpGlobal.State.ForwardingDelay = &forDelay + stpGlobal.State.HelloTime = &helloTime + stpGlobal.State.MaxAge = &maxAge + stpGlobal.State.RootguardTimeout = &rootGTimeout + stpGlobal.State.BpduFilter = &bpduFilter + } + } +} + +///////////////// RPVST ////////////////////// +func (app *StpApp) convertOCRpvstConfToInternal(opcode int) error { + var err error + stp := app.getAppRootObject() + setDefaultFlag := (opcode == CREATE || opcode == REPLACE) + if stp != nil && stp.RapidPvst != nil && len(stp.RapidPvst.Vlan) > 0 { + for vlanId, _ := range stp.RapidPvst.Vlan { + vlanName := "Vlan" + strconv.Itoa(int(vlanId)) + app.vlanTableMap[vlanName] = db.Value{Field: map[string]string{}} + rpvstVlanConf := stp.RapidPvst.Vlan[vlanId] + if rpvstVlanConf.Config != nil { + dbVal := app.vlanTableMap[vlanName] + (&dbVal).Set("vlanid", strconv.Itoa(int(vlanId))) + if rpvstVlanConf.Config.BridgePriority != nil { + priorityVal := int(*rpvstVlanConf.Config.BridgePriority) + if (priorityVal % 4096) != 0 { + return tlerr.InvalidArgs("Priority value should be multiple of 4096") + } + (&dbVal).Set("priority", strconv.Itoa(priorityVal)) + } else if setDefaultFlag { + (&dbVal).Set("priority", "32768") + } + if rpvstVlanConf.Config.ForwardingDelay != nil { + (&dbVal).Set("forward_delay", strconv.Itoa(int(*rpvstVlanConf.Config.ForwardingDelay))) + } else if setDefaultFlag { + (&dbVal).Set("forward_delay", "15") + } + if rpvstVlanConf.Config.HelloTime != nil { + (&dbVal).Set("hello_time", strconv.Itoa(int(*rpvstVlanConf.Config.HelloTime))) + } else if setDefaultFlag { + (&dbVal).Set("hello_time", "2") + } + if rpvstVlanConf.Config.MaxAge != nil { + (&dbVal).Set("max_age", strconv.Itoa(int(*rpvstVlanConf.Config.MaxAge))) + } else if setDefaultFlag { + (&dbVal).Set("max_age", "20") + } + if rpvstVlanConf.Config.SpanningTreeEnable != nil { + if *rpvstVlanConf.Config.SpanningTreeEnable { + (&dbVal).Set("enabled", "true") + } else { + (&dbVal).Set("enabled", "false") + } + } else if setDefaultFlag { + (&dbVal).Set("enabled", "false") + } + } + if rpvstVlanConf.Interfaces != nil && len(rpvstVlanConf.Interfaces.Interface) > 0 { + app.vlanIntfTableMap[vlanName] = make(map[string]db.Value) + for intfId, _ := range rpvstVlanConf.Interfaces.Interface { + rpvstVlanIntfConf := rpvstVlanConf.Interfaces.Interface[intfId] + app.vlanIntfTableMap[vlanName][intfId] = db.Value{Field: map[string]string{}} + if rpvstVlanIntfConf.Config != nil { + dbVal := app.vlanIntfTableMap[vlanName][intfId] + if rpvstVlanIntfConf.Config.Cost != nil { + (&dbVal).Set("path_cost", strconv.Itoa(int(*rpvstVlanIntfConf.Config.Cost))) + } else if setDefaultFlag { + (&dbVal).Set("path_cost", "200") + } + if rpvstVlanIntfConf.Config.PortPriority != nil { + (&dbVal).Set("priority", strconv.Itoa(int(*rpvstVlanIntfConf.Config.PortPriority))) + } else if setDefaultFlag { + (&dbVal).Set("priority", "128") + } + } + } + } + } + } + return err +} + +func (app *StpApp) setRpvstVlanDataInDB(d *db.DB, createFlag bool) error { + var err error + for vlanName := range app.vlanTableMap { + existingEntry, err := d.GetEntry(app.vlanTable, asKey(vlanName)) + if createFlag && existingEntry.IsPopulated() { + return tlerr.AlreadyExists("Vlan %s already configured", vlanName) + } + if createFlag || (!createFlag && err != nil && !existingEntry.IsPopulated()) { + err = d.CreateEntry(app.vlanTable, asKey(vlanName), app.vlanTableMap[vlanName]) + } else { + if existingEntry.IsPopulated() { + err = d.ModEntry(app.vlanTable, asKey(vlanName), app.vlanTableMap[vlanName]) + } + } + } + return err +} + +func (app *StpApp) setRpvstVlanInterfaceDataInDB(d *db.DB, createFlag bool) error { + var err error + for vlanName := range app.vlanIntfTableMap { + for intfId := range app.vlanIntfTableMap[vlanName] { + existingEntry, err := d.GetEntry(app.vlanIntfTable, asKey(vlanName, intfId)) + if createFlag && existingEntry.IsPopulated() { + return tlerr.AlreadyExists("Interface %s already configured", intfId) + } + if createFlag || (!createFlag && err != nil && !existingEntry.IsPopulated()) { + err = d.CreateEntry(app.vlanIntfTable, asKey(vlanName, intfId), app.vlanIntfTableMap[vlanName][intfId]) + } else { + if existingEntry.IsPopulated() { + err = d.ModEntry(app.vlanIntfTable, asKey(vlanName, intfId), app.vlanIntfTableMap[vlanName][intfId]) + } + } + } + } + return err +} + +func (app *StpApp) convertDBRpvstVlanConfigToInternal(d *db.DB, vlanKey db.Key) error { + var err error + if vlanKey.Len() > 0 { + entry, err := d.GetEntry(app.vlanTable, vlanKey) + if err != nil { + return err + } + vlanName := vlanKey.Get(0) + if entry.IsPopulated() { + app.vlanTableMap[vlanName] = entry + app.vlanIntfTableMap[vlanName] = make(map[string]db.Value) + err = app.convertDBRpvstVlanInterfaceToInternal(d, vlanName, "", db.Key{}, false) + if err != nil { + return err + } + // Collect operational info from application DB + err = app.convertApplDBRpvstVlanToInternal(vlanName) + } else { + return tlerr.NotFound("Vlan %s is not configured", vlanName) + } + } else { + tbl, err := d.GetTable(app.vlanTable) + if err != nil { + return err + } + keys, err := tbl.GetKeys() + if err != nil { + return err + } + for i, _ := range keys { + app.convertDBRpvstVlanConfigToInternal(d, keys[i]) + } + } + + return err +} + +func (app *StpApp) convertInternalToOCRpvstVlanConfig(vlanName string, rpvst *ocbinds.OpenconfigSpanningTree_Stp_RapidPvst, rpvstVlanConf *ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan) { + if len(vlanName) > 0 { + var num uint64 + if rpvstVlanConf != nil { + if rpvstVlanData, ok := app.vlanTableMap[vlanName]; ok { + vlanId, _ := strconv.Atoi(strings.Replace(vlanName, "Vlan", "", 1)) + vlan := uint16(vlanId) + rpvstVlanConf.VlanId = &vlan + rpvstVlanConf.Config.VlanId = &vlan + rpvstVlanConf.State.VlanId = &vlan + + stpEnabled, _ := strconv.ParseBool((&rpvstVlanData).Get("enabled")) + rpvstVlanConf.Config.SpanningTreeEnable = &stpEnabled + //rpvstVlanConf.State.SpanningTreeEnable = &stpEnabled + + num, _ = strconv.ParseUint((&rpvstVlanData).Get("priority"), 10, 32) + priority := uint32(num) + rpvstVlanConf.Config.BridgePriority = &priority + rpvstVlanConf.State.BridgePriority = &priority + + num, _ = strconv.ParseUint((&rpvstVlanData).Get("forward_delay"), 10, 8) + forDelay := uint8(num) + rpvstVlanConf.Config.ForwardingDelay = &forDelay + + num, _ = strconv.ParseUint((&rpvstVlanData).Get("hello_time"), 10, 8) + helloTime := uint8(num) + rpvstVlanConf.Config.HelloTime = &helloTime + + num, _ = strconv.ParseUint((&rpvstVlanData).Get("max_age"), 10, 8) + maxAge := uint8(num) + rpvstVlanConf.Config.MaxAge = &maxAge + } + + // populate operational information + //ygot.BuildEmptyTree(rpvstVlanConf.State) + operDbVal := app.vlanOperTableMap[vlanName] + if operDbVal.IsPopulated() { + num, _ = strconv.ParseUint((&operDbVal).Get("max_age"), 10, 8) + opMaxAge := uint8(num) + rpvstVlanConf.State.MaxAge = &opMaxAge + + num, _ = strconv.ParseUint((&operDbVal).Get("hello_time"), 10, 8) + opHelloTime := uint8(num) + rpvstVlanConf.State.HelloTime = &opHelloTime + + num, _ = strconv.ParseUint((&operDbVal).Get("forward_delay"), 10, 8) + opForwardDelay := uint8(num) + rpvstVlanConf.State.ForwardingDelay = &opForwardDelay + + num, _ = strconv.ParseUint((&operDbVal).Get("hold_time"), 10, 8) + opHoldTime := uint8(num) + rpvstVlanConf.State.HoldTime = &opHoldTime + + /*num, _ = strconv.ParseUint((&operDbVal).Get("root_max_age"), 10, 8) + opRootMaxAge := uint8(num) + rpvstVlanConf.State.RootMaxAge = &opRootMaxAge + + num, _ = strconv.ParseUint((&operDbVal).Get("root_hello_time"), 10, 8) + opRootHelloTime := uint8(num) + rpvstVlanConf.State.RootHelloTime = &opRootHelloTime + + num, _ = strconv.ParseUint((&operDbVal).Get("root_forward_delay"), 10, 8) + opRootForwardDelay := uint8(num) + rpvstVlanConf.State.RootForwardingDelay = &opRootForwardDelay */ + + num, _ = strconv.ParseUint((&operDbVal).Get("stp_instance"), 10, 16) + opStpInstance := uint16(num) + rpvstVlanConf.State.StpInstance = &opStpInstance + + num, _ = strconv.ParseUint((&operDbVal).Get("root_path_cost"), 10, 32) + opRootCost := uint32(num) + rpvstVlanConf.State.RootCost = &opRootCost + + num, _ = strconv.ParseUint((&operDbVal).Get("last_topology_change"), 10, 64) + opLastTopologyChange := num + rpvstVlanConf.State.LastTopologyChange = &opLastTopologyChange + + num, _ = strconv.ParseUint((&operDbVal).Get("topology_change_count"), 10, 64) + opTopologyChanges := num + rpvstVlanConf.State.TopologyChanges = &opTopologyChanges + + bridgeId := (&operDbVal).Get("bridge_id") + rpvstVlanConf.State.BridgeAddress = &bridgeId + + desigRootAddr := (&operDbVal).Get("desig_bridge_id") + rpvstVlanConf.State.DesignatedRootAddress = &desigRootAddr + + rootPortStr := (&operDbVal).Get("root_port") + rpvstVlanConf.State.RootPortName = &rootPortStr + } + + app.convertInternalToOCRpvstVlanInterface(vlanName, "", rpvstVlanConf, nil) + // populate operational information + app.convertOperInternalToOCVlanInterface(vlanName, "", rpvstVlanConf, nil) + } + } else { + for vlanName := range app.vlanTableMap { + vlanId, _ := strconv.Atoi(strings.Replace(vlanName, "Vlan", "", 1)) + vlan := uint16(vlanId) + + rpvstVlanConfPtr, _ := rpvst.NewVlan(vlan) + ygot.BuildEmptyTree(rpvstVlanConfPtr) + app.convertInternalToOCRpvstVlanConfig(vlanName, rpvst, rpvstVlanConfPtr) + } + } +} + +func (app *StpApp) convertDBRpvstVlanInterfaceToInternal(d *db.DB, vlanName string, intfId string, vlanInterfaceKey db.Key, doGetOperData bool) error { + var err error + if vlanInterfaceKey.Len() > 1 { + rpvstVlanIntfConf, err := d.GetEntry(app.vlanIntfTable, asKey(vlanName, intfId)) + if app.vlanIntfTableMap[vlanName] == nil { + app.vlanIntfTableMap[vlanName] = make(map[string]db.Value) + } + if err == nil { + app.vlanIntfTableMap[vlanName][intfId] = rpvstVlanIntfConf + } + // Collect operational info from application DB + if doGetOperData { + err = app.convertApplDBRpvstVlanInterfaceToInternal(vlanName, intfId) + } + if err != nil { + return err + } + } else { + keys, err := d.GetKeys(app.vlanIntfTable) + if err != nil { + return err + } + for i, _ := range keys { + if vlanName == keys[i].Get(0) { + err = app.convertDBRpvstVlanInterfaceToInternal(d, vlanName, keys[i].Get(1), keys[i], doGetOperData) + } + } + } + return err +} + +func (app *StpApp) convertInternalToOCRpvstVlanInterface(vlanName string, intfId string, rpvstVlanConf *ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan, rpvstVlanIntfConf *ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan_Interfaces_Interface) { + var num uint64 + + if len(intfId) == 0 { + for intf, _ := range app.vlanIntfTableMap[vlanName] { + app.convertInternalToOCRpvstVlanInterface(vlanName, intf, rpvstVlanConf, rpvstVlanIntfConf) + } + } else { + dbVal := app.vlanIntfTableMap[vlanName][intfId] + + if rpvstVlanIntfConf == nil { + if rpvstVlanConf != nil { + rpvstVlanIntfConf_, _ := rpvstVlanConf.Interfaces.NewInterface(intfId) + rpvstVlanIntfConf = rpvstVlanIntfConf_ + ygot.BuildEmptyTree(rpvstVlanIntfConf) + } + } + + var err error + num, err = strconv.ParseUint((&dbVal).Get("path_cost"), 10, 32) + if err == nil { + cost := uint32(num) + rpvstVlanIntfConf.Config.Cost = &cost + } + + num, err = strconv.ParseUint((&dbVal).Get("priority"), 10, 8) + if err == nil { + portPriority := uint8(num) + rpvstVlanIntfConf.Config.PortPriority = &portPriority + } + + rpvstVlanIntfConf.Config.Name = &intfId + } +} + +/////////// PVST ////////////////////// +func (app *StpApp) convertOCPvstToInternal(opcode int) error { + var err error + stp := app.getAppRootObject() + setDefaultFlag := (opcode == CREATE || opcode == REPLACE) + if stp != nil && stp.Pvst != nil && len(stp.Pvst.Vlan) > 0 { + for vlanId, _ := range stp.Pvst.Vlan { + vlanName := "Vlan" + strconv.Itoa(int(vlanId)) + app.vlanTableMap[vlanName] = db.Value{Field: map[string]string{}} + pvstVlan := stp.Pvst.Vlan[vlanId] + if pvstVlan.Config != nil { + dbVal := app.vlanTableMap[vlanName] + (&dbVal).Set("vlanid", strconv.Itoa(int(vlanId))) + if pvstVlan.Config.BridgePriority != nil { + priorityVal := int(*pvstVlan.Config.BridgePriority) + if (priorityVal % 4096) != 0 { + return tlerr.InvalidArgs("Priority value should be multiple of 4096") + } + (&dbVal).Set("priority", strconv.Itoa(priorityVal)) + } else if setDefaultFlag { + (&dbVal).Set("priority", "32768") + } + if pvstVlan.Config.ForwardingDelay != nil { + (&dbVal).Set("forward_delay", strconv.Itoa(int(*pvstVlan.Config.ForwardingDelay))) + } else if setDefaultFlag { + (&dbVal).Set("forward_delay", "15") + } + if pvstVlan.Config.HelloTime != nil { + (&dbVal).Set("hello_time", strconv.Itoa(int(*pvstVlan.Config.HelloTime))) + } else if setDefaultFlag { + (&dbVal).Set("hello_time", "2") + } + if pvstVlan.Config.MaxAge != nil { + (&dbVal).Set("max_age", strconv.Itoa(int(*pvstVlan.Config.MaxAge))) + } else if setDefaultFlag { + (&dbVal).Set("max_age", "20") + } + if pvstVlan.Config.SpanningTreeEnable != nil { + if *pvstVlan.Config.SpanningTreeEnable { + (&dbVal).Set("enabled", "true") + } else { + (&dbVal).Set("enabled", "false") + } + } else if setDefaultFlag { + (&dbVal).Set("enabled", "false") + } + } + if pvstVlan.Interfaces != nil && len(pvstVlan.Interfaces.Interface) > 0 { + app.vlanIntfTableMap[vlanName] = make(map[string]db.Value) + for intfId, _ := range pvstVlan.Interfaces.Interface { + pvstVlanIntf := pvstVlan.Interfaces.Interface[intfId] + app.vlanIntfTableMap[vlanName][intfId] = db.Value{Field: map[string]string{}} + if pvstVlanIntf.Config != nil { + dbVal := app.vlanIntfTableMap[vlanName][intfId] + if pvstVlanIntf.Config.Cost != nil { + (&dbVal).Set("path_cost", strconv.Itoa(int(*pvstVlanIntf.Config.Cost))) + } else if setDefaultFlag { + (&dbVal).Set("path_cost", "200") + } + if pvstVlanIntf.Config.PortPriority != nil { + (&dbVal).Set("priority", strconv.Itoa(int(*pvstVlanIntf.Config.PortPriority))) + } else if setDefaultFlag { + (&dbVal).Set("priority", "128") + } + } + } + } + } + } + return err +} + +func (app *StpApp) convertInternalToOCPvstVlan(vlanName string, pvst *ocbinds.OpenconfigSpanningTree_Stp_Pvst, pvstVlan *ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan) { + if len(vlanName) > 0 { + var num uint64 + if pvstVlan != nil { + if pvstVlanData, ok := app.vlanTableMap[vlanName]; ok { + vlanId, _ := strconv.Atoi(strings.Replace(vlanName, "Vlan", "", 1)) + vlan := uint16(vlanId) + pvstVlan.VlanId = &vlan + pvstVlan.Config.VlanId = &vlan + pvstVlan.State.VlanId = &vlan + + stpEnabled, _ := strconv.ParseBool((&pvstVlanData).Get("enabled")) + pvstVlan.Config.SpanningTreeEnable = &stpEnabled + //pvstVlan.State.SpanningTreeEnable = &stpEnabled + + num, _ = strconv.ParseUint((&pvstVlanData).Get("priority"), 10, 32) + priority := uint32(num) + pvstVlan.Config.BridgePriority = &priority + pvstVlan.State.BridgePriority = &priority + + num, _ = strconv.ParseUint((&pvstVlanData).Get("forward_delay"), 10, 8) + forDelay := uint8(num) + pvstVlan.Config.ForwardingDelay = &forDelay + + num, _ = strconv.ParseUint((&pvstVlanData).Get("hello_time"), 10, 8) + helloTime := uint8(num) + pvstVlan.Config.HelloTime = &helloTime + + num, _ = strconv.ParseUint((&pvstVlanData).Get("max_age"), 10, 8) + maxAge := uint8(num) + pvstVlan.Config.MaxAge = &maxAge + } + + // populate operational information + operDbVal := app.vlanOperTableMap[vlanName] + if operDbVal.IsPopulated() { + num, _ = strconv.ParseUint((&operDbVal).Get("max_age"), 10, 8) + opMaxAge := uint8(num) + pvstVlan.State.MaxAge = &opMaxAge + + num, _ = strconv.ParseUint((&operDbVal).Get("hello_time"), 10, 8) + opHelloTime := uint8(num) + pvstVlan.State.HelloTime = &opHelloTime + + num, _ = strconv.ParseUint((&operDbVal).Get("forward_delay"), 10, 8) + opForwardDelay := uint8(num) + pvstVlan.State.ForwardingDelay = &opForwardDelay + + num, _ = strconv.ParseUint((&operDbVal).Get("hold_time"), 10, 8) + opHoldTime := uint8(num) + pvstVlan.State.HoldTime = &opHoldTime + + /*num, _ = strconv.ParseUint((&operDbVal).Get("root_max_age"), 10, 8) + opRootMaxAge := uint8(num) + pvstVlan.State.RootMaxAge = &opRootMaxAge + + num, _ = strconv.ParseUint((&operDbVal).Get("root_hello_time"), 10, 8) + opRootHelloTime := uint8(num) + pvstVlan.State.RootHelloTime = &opRootHelloTime + + num, _ = strconv.ParseUint((&operDbVal).Get("root_forward_delay"), 10, 8) + opRootForwardDelay := uint8(num) + pvstVlan.State.RootForwardingDelay = &opRootForwardDelay */ + + num, _ = strconv.ParseUint((&operDbVal).Get("stp_instance"), 10, 16) + opStpInstance := uint16(num) + pvstVlan.State.StpInstance = &opStpInstance + + num, _ = strconv.ParseUint((&operDbVal).Get("root_path_cost"), 10, 32) + opRootCost := uint32(num) + pvstVlan.State.RootCost = &opRootCost + + num, _ = strconv.ParseUint((&operDbVal).Get("last_topology_change"), 10, 64) + opLastTopologyChange := num + pvstVlan.State.LastTopologyChange = &opLastTopologyChange + + num, _ = strconv.ParseUint((&operDbVal).Get("topology_change_count"), 10, 64) + opTopologyChanges := num + pvstVlan.State.TopologyChanges = &opTopologyChanges + + bridgeId := (&operDbVal).Get("bridge_id") + pvstVlan.State.BridgeAddress = &bridgeId + + desigRootAddr := (&operDbVal).Get("desig_bridge_id") + pvstVlan.State.DesignatedRootAddress = &desigRootAddr + + rootPortStr := (&operDbVal).Get("root_port") + pvstVlan.State.RootPortName = &rootPortStr + } + + app.convertInternalToOCPvstVlanInterface(vlanName, "", pvstVlan, nil) + // populate operational information + app.convertOperInternalToOCVlanInterface(vlanName, "", pvstVlan, nil) + } + } else { + for vlanName := range app.vlanTableMap { + vlanId, _ := strconv.Atoi(strings.Replace(vlanName, "Vlan", "", 1)) + vlan := uint16(vlanId) + + pvstVlanPtr, _ := pvst.NewVlan(vlan) + ygot.BuildEmptyTree(pvstVlanPtr) + app.convertInternalToOCPvstVlan(vlanName, pvst, pvstVlanPtr) + } + } +} + +func (app *StpApp) convertInternalToOCPvstVlanInterface(vlanName string, intfId string, pvstVlan *ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan, pvstVlanIntf *ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan_Interfaces_Interface) { + var num uint64 + + if len(intfId) == 0 { + for intf, _ := range app.vlanIntfTableMap[vlanName] { + app.convertInternalToOCPvstVlanInterface(vlanName, intf, pvstVlan, pvstVlanIntf) + } + } else { + dbVal := app.vlanIntfTableMap[vlanName][intfId] + + if pvstVlanIntf == nil { + if pvstVlan != nil { + pvstVlanIntf_, _ := pvstVlan.Interfaces.NewInterface(intfId) + pvstVlanIntf = pvstVlanIntf_ + ygot.BuildEmptyTree(pvstVlanIntf) + } + } + + var err error + num, err = strconv.ParseUint((&dbVal).Get("path_cost"), 10, 32) + if err == nil { + cost := uint32(num) + pvstVlanIntf.Config.Cost = &cost + } + + num, err = strconv.ParseUint((&dbVal).Get("priority"), 10, 8) + if err == nil { + portPriority := uint8(num) + pvstVlanIntf.Config.PortPriority = &portPriority + } + + pvstVlanIntf.Config.Name = &intfId + } +} + +/////////// Interfaces ////////// +func (app *StpApp) convertOCStpInterfacesToInternal() { + stp := app.getAppRootObject() + if stp != nil && stp.Interfaces != nil && len(stp.Interfaces.Interface) > 0 { + for intfId, _ := range stp.Interfaces.Interface { + app.intfTableMap[intfId] = db.Value{Field: map[string]string{}} + + stpIntfConf := stp.Interfaces.Interface[intfId] + if stpIntfConf.Config != nil { + dbVal := app.intfTableMap[intfId] + + if stpIntfConf.Config.BpduGuard != nil { + if *stpIntfConf.Config.BpduGuard == true { + (&dbVal).Set("bpdu_guard", "true") + } else { + (&dbVal).Set("bpdu_guard", "false") + } + } + + if stpIntfConf.Config.BpduFilter != nil { + if *stpIntfConf.Config.BpduFilter == true { + (&dbVal).Set("bpdu_filter", "enable") + } else { + (&dbVal).Set("bpdu_filter", "disable") + } + } else { + (&dbVal).Set("bpdu_filter", "global") + } + + if stpIntfConf.Config.BpduGuardPortShutdown != nil { + if *stpIntfConf.Config.BpduGuardPortShutdown == true { + (&dbVal).Set("bpdu_guard_do_disable", "true") + } else { + (&dbVal).Set("bpdu_guard_do_disable", "false") + } + } + + if stpIntfConf.Config.Portfast != nil { + if *stpIntfConf.Config.Portfast == true { + (&dbVal).Set("portfast", "true") + } else { + (&dbVal).Set("portfast", "false") + } + } + + if stpIntfConf.Config.UplinkFast != nil { + if *stpIntfConf.Config.UplinkFast == true { + (&dbVal).Set("uplink_fast", "true") + } else { + (&dbVal).Set("uplink_fast", "false") + } + } + + if stpIntfConf.Config.SpanningTreeEnable != nil { + if *stpIntfConf.Config.SpanningTreeEnable == true { + (&dbVal).Set("enabled", "true") + } else { + (&dbVal).Set("enabled", "false") + } + } + + if stpIntfConf.Config.Cost != nil { + (&dbVal).Set("path_cost", strconv.Itoa(int(*stpIntfConf.Config.Cost))) + } else { + //(&dbVal).Set("path_cost", "200") + } + if stpIntfConf.Config.PortPriority != nil { + (&dbVal).Set("priority", strconv.Itoa(int(*stpIntfConf.Config.PortPriority))) + } else { + //(&dbVal).Set("priority", "128") + } + + if stpIntfConf.Config.Guard == ocbinds.OpenconfigSpanningTree_StpGuardType_ROOT { + (&dbVal).Set("root_guard", "true") + } else { + //(&dbVal).Set("root_guard", "false") + } + //// For RPVST+ ///// + if stpIntfConf.Config.EdgePort == ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_ENABLE { + (&dbVal).Set("edge_port", "true") + } else if stpIntfConf.Config.EdgePort == ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_DISABLE { + (&dbVal).Set("edge_port", "false") + } + + if stpIntfConf.Config.LinkType == ocbinds.OpenconfigSpanningTree_StpLinkType_P2P { + (&dbVal).Set("link_type", "point-to-point") + } else if stpIntfConf.Config.LinkType == ocbinds.OpenconfigSpanningTree_StpLinkType_SHARED { + (&dbVal).Set("link_type", "shared") + } else { + (&dbVal).Set("link_type", "auto") + } + } + } + } +} + +func (app *StpApp) setStpInterfacesDataInDB(d *db.DB, createFlag bool) error { + var err error + // Fetch list of interfaces which are L2 i.e. members of any Vlan + l2IntfList, err1 := app.getAllInterfacesFromVlanMemberTable(d) + if err1 != nil { + return tlerr.InvalidArgs("There are no L2 interfaces configured") + } + // Fetch list of interfaces which are members of port-channel + poMemberIntfList, _ := app.getAllInterfacesFromPortChannelMemberTable(d) + + for intfName := range app.intfTableMap { + if !contains(l2IntfList, intfName) { + return tlerr.InvalidArgs("%s has no VLAN configured - It's not a L2 interface", intfName) + } + if contains(poMemberIntfList, intfName) { + return tlerr.InvalidArgs("%s is a portchannel member port - STP can't be configured", intfName) + } + existingEntry, err := d.GetEntry(app.interfaceTable, asKey(intfName)) + if createFlag && existingEntry.IsPopulated() { + return tlerr.AlreadyExists("Stp Interface %s already configured", intfName) + } + if createFlag || (!createFlag && err != nil && !existingEntry.IsPopulated()) { + err = d.CreateEntry(app.interfaceTable, asKey(intfName), app.intfTableMap[intfName]) + } else { + if existingEntry.IsPopulated() { + err = d.ModEntry(app.interfaceTable, asKey(intfName), app.intfTableMap[intfName]) + } + } + } + return err +} + +func (app *StpApp) convertDBStpInterfacesToInternal(d *db.DB, intfKey db.Key) error { + var err error + if intfKey.Len() > 0 { + entry, err := d.GetEntry(app.interfaceTable, intfKey) + if err != nil { + return err + } + intfName := intfKey.Get(0) + if entry.IsPopulated() { + app.intfTableMap[intfName] = entry + } else { + return tlerr.NotFound("STP interface %s is not configured", intfName) + } + err = app.convertApplDBStpInterfacesToInternal(intfName) + } else { + tbl, err := d.GetTable(app.interfaceTable) + if err != nil { + return err + } + keys, err := tbl.GetKeys() + if err != nil { + return err + } + for i, _ := range keys { + app.convertDBStpInterfacesToInternal(d, keys[i]) + } + } + + return err +} + +func (app *StpApp) convertInternalToOCStpInterfaces(intfName string, interfaces *ocbinds.OpenconfigSpanningTree_Stp_Interfaces, intf *ocbinds.OpenconfigSpanningTree_Stp_Interfaces_Interface) { + var err error + if len(intfName) > 0 { + if stpIntfData, ok := app.intfTableMap[intfName]; ok { + if intf != nil { + intf.Config.Name = &intfName + intf.State.Name = &intfName + + stpEnabled, _ := strconv.ParseBool((&stpIntfData).Get("enabled")) + intf.Config.SpanningTreeEnable = &stpEnabled + intf.State.SpanningTreeEnable = &stpEnabled + + bpduGuardEnabled, _ := strconv.ParseBool((&stpIntfData).Get("bpdu_guard")) + intf.Config.BpduGuard = &bpduGuardEnabled + intf.State.BpduGuard = &bpduGuardEnabled + + var bpduFilterEnabled bool + bpduFilterVal := (&stpIntfData).Get("bpdu_filter") + if bpduFilterVal == "enable" { + bpduFilterEnabled = true + intf.Config.BpduFilter = &bpduFilterEnabled + intf.State.BpduFilter = &bpduFilterEnabled + } else if bpduFilterVal == "disable" { + bpduFilterEnabled = false + intf.Config.BpduFilter = &bpduFilterEnabled + intf.State.BpduFilter = &bpduFilterEnabled + } + + bpduGuardPortShut, _ := strconv.ParseBool((&stpIntfData).Get("bpdu_guard_do_disable")) + intf.Config.BpduGuardPortShutdown = &bpduGuardPortShut + intf.State.BpduGuardPortShutdown = &bpduGuardPortShut + + uplinkFast, _ := strconv.ParseBool((&stpIntfData).Get("uplink_fast")) + intf.Config.UplinkFast = &uplinkFast + intf.State.UplinkFast = &uplinkFast + + portFast, _ := strconv.ParseBool((&stpIntfData).Get("portfast")) + intf.Config.Portfast = &portFast + + rootGuardEnabled, _ := strconv.ParseBool((&stpIntfData).Get("root_guard")) + if rootGuardEnabled { + intf.Config.Guard = ocbinds.OpenconfigSpanningTree_StpGuardType_ROOT + intf.State.Guard = ocbinds.OpenconfigSpanningTree_StpGuardType_ROOT + } else { + intf.Config.Guard = ocbinds.OpenconfigSpanningTree_StpGuardType_NONE + intf.State.Guard = ocbinds.OpenconfigSpanningTree_StpGuardType_NONE + } + + if edgePortEnabled, err := strconv.ParseBool((&stpIntfData).Get("edge_port")); err == nil { + if edgePortEnabled { + intf.Config.EdgePort = ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_ENABLE + intf.State.EdgePort = ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_ENABLE + } else { + intf.Config.EdgePort = ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_DISABLE + intf.State.EdgePort = ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_DISABLE + } + } + + linkTypeVal := (&stpIntfData).Get("link_type") + switch linkTypeVal { + case "shared": + intf.Config.LinkType = ocbinds.OpenconfigSpanningTree_StpLinkType_SHARED + intf.State.LinkType = ocbinds.OpenconfigSpanningTree_StpLinkType_SHARED + case "point-to-point": + intf.Config.LinkType = ocbinds.OpenconfigSpanningTree_StpLinkType_P2P + intf.State.LinkType = ocbinds.OpenconfigSpanningTree_StpLinkType_P2P + } + + var num uint64 + if num, err = strconv.ParseUint((&stpIntfData).Get("priority"), 10, 8); err == nil { + priority := uint8(num) + intf.Config.PortPriority = &priority + intf.State.PortPriority = &priority + } + + if num, err = strconv.ParseUint((&stpIntfData).Get("path_cost"), 10, 32); err == nil { + cost := uint32(num) + intf.Config.Cost = &cost + intf.State.Cost = &cost + } + } + } + + operDbVal := app.intfOperTableMap[intfName] + if operDbVal.IsPopulated() && intf != nil { + var boolVal bool + + bpduGuardShut := (&operDbVal).Get("bpdu_guard_shutdown") + if bpduGuardShut == "yes" { + boolVal = true + } else if bpduGuardShut == "no" { + boolVal = false + } + intf.State.BpduGuardShutdown = &boolVal + + opPortfast := (&operDbVal).Get("port_fast") + if opPortfast == "yes" { + boolVal = true + } else if opPortfast == "no" { + boolVal = false + } + intf.State.Portfast = &boolVal + + opBpduFilter := (&operDbVal).Get("bpdu_filter") + if opBpduFilter == "yes" { + boolVal = true + } else if opBpduFilter == "no" { + boolVal = false + } + intf.State.BpduFilter = &boolVal + + opEdgePortType := (&operDbVal).Get("edge_port") + if opEdgePortType == "yes" { + intf.State.EdgePort = ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_ENABLE + } else if opEdgePortType == "no" { + intf.State.EdgePort = ocbinds.OpenconfigSpanningTreeTypes_STP_EDGE_PORT_EDGE_DISABLE + } + + opLinkType := (&operDbVal).Get("link_type") + if opLinkType == "shared" { + intf.State.LinkType = ocbinds.OpenconfigSpanningTree_StpLinkType_SHARED + } else if opLinkType == "point-to-point" { + intf.State.LinkType = ocbinds.OpenconfigSpanningTree_StpLinkType_P2P + } + } + } else { + for intfName := range app.intfTableMap { + intfPtr, _ := interfaces.NewInterface(intfName) + ygot.BuildEmptyTree(intfPtr) + app.convertInternalToOCStpInterfaces(intfName, interfaces, intfPtr) + } + } +} + +func (app *StpApp) convertOperInternalToOCVlanInterface(vlanName string, intfId string, vlan interface{}, vlanIntf interface{}) { + if len(intfId) > 0 { + var pvstVlan *ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan + var pvstVlanIntf *ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan_Interfaces_Interface + var rpvstVlan *ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan + var rpvstVlanIntf *ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan_Interfaces_Interface + var num uint64 + + switch reflect.TypeOf(vlan).Elem().Name() { + case "OpenconfigSpanningTree_Stp_Pvst_Vlan": + pvstVlan, _ = vlan.(*ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan) + if vlanIntf == nil { + pvstVlanIntf = pvstVlan.Interfaces.Interface[intfId] + if pvstVlanIntf == nil { + pvstVlanIntf, _ = pvstVlan.Interfaces.NewInterface(intfId) + } + ygot.BuildEmptyTree(pvstVlanIntf) + } else { + pvstVlanIntf, _ = vlanIntf.(*ocbinds.OpenconfigSpanningTree_Stp_Pvst_Vlan_Interfaces_Interface) + } + ygot.BuildEmptyTree(pvstVlanIntf.State) + case "OpenconfigSpanningTree_Stp_RapidPvst_Vlan": + rpvstVlan, _ = vlan.(*ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan) + if vlanIntf == nil { + rpvstVlanIntf = rpvstVlan.Interfaces.Interface[intfId] + if rpvstVlanIntf == nil { + rpvstVlanIntf, _ = rpvstVlan.Interfaces.NewInterface(intfId) + } + ygot.BuildEmptyTree(rpvstVlanIntf) + } else { + rpvstVlanIntf, _ = vlanIntf.(*ocbinds.OpenconfigSpanningTree_Stp_RapidPvst_Vlan_Interfaces_Interface) + } + ygot.BuildEmptyTree(rpvstVlanIntf.State) + } + + operDbVal := app.vlanIntfOperTableMap[vlanName][intfId] + + if operDbVal.IsPopulated() { + num, _ = strconv.ParseUint((&operDbVal).Get("port_num"), 10, 16) + opPortNum := uint16(num) + + num, _ = strconv.ParseUint((&operDbVal).Get("path_cost"), 10, 32) + opcost := uint32(num) + + num, _ = strconv.ParseUint((&operDbVal).Get("priority"), 10, 8) + opPortPriority := uint8(num) + + num, _ = strconv.ParseUint((&operDbVal).Get("desig_cost"), 10, 32) + opDesigCost := uint32(num) + + num, _ = strconv.ParseUint((&operDbVal).Get("desig_port"), 10, 16) + opDesigPortNum := uint16(num) + + num, _ = strconv.ParseUint((&operDbVal).Get("root_guard_timer"), 10, 16) + opRootGuardTimer := uint16(num) + + num, _ = strconv.ParseUint((&operDbVal).Get("fwd_transitions"), 10, 64) + opFwtrans := num + + desigRootAddr := (&operDbVal).Get("desig_root") + + desigBridgeAddr := (&operDbVal).Get("desig_bridge") + + portState := (&operDbVal).Get("port_state") + + //Counters + num, _ = strconv.ParseUint((&operDbVal).Get("bpdu_sent"), 10, 64) + opBpduSent := num + + num, _ = strconv.ParseUint((&operDbVal).Get("bpdu_received"), 10, 64) + opBpduReceived := num + + num, _ = strconv.ParseUint((&operDbVal).Get("tcn_sent"), 10, 64) + opTcnSent := num + + num, _ = strconv.ParseUint((&operDbVal).Get("tcn_received"), 10, 64) + opTcnReceived := num + + // For RPVST+ only + num, _ = strconv.ParseUint((&operDbVal).Get("config_bpdu_sent"), 10, 64) + opConfigBpduSent := num + + num, _ = strconv.ParseUint((&operDbVal).Get("config_bpdu_received"), 10, 64) + opConfigBpduReceived := num + + if pvstVlanIntf != nil && pvstVlanIntf.State != nil { + pvstVlanIntf.State.Name = &intfId + pvstVlanIntf.State.PortNum = &opPortNum + pvstVlanIntf.State.Cost = &opcost + pvstVlanIntf.State.PortPriority = &opPortPriority + pvstVlanIntf.State.DesignatedCost = &opDesigCost + pvstVlanIntf.State.DesignatedPortNum = &opDesigPortNum + pvstVlanIntf.State.RootGuardTimer = &opRootGuardTimer + pvstVlanIntf.State.ForwardTransisitions = &opFwtrans + pvstVlanIntf.State.DesignatedRootAddress = &desigRootAddr + pvstVlanIntf.State.DesignatedBridgeAddress = &desigBridgeAddr + switch portState { + case "DISABLED": + pvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_DISABLED + case "BLOCKING": + pvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_BLOCKING + case "LISTENING": + pvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_LISTENING + case "LEARNING": + pvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_LEARNING + case "FORWARDING": + pvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_FORWARDING + } + if pvstVlanIntf.State.Counters != nil { + pvstVlanIntf.State.Counters.BpduSent = &opBpduSent + pvstVlanIntf.State.Counters.BpduReceived = &opBpduReceived + pvstVlanIntf.State.Counters.TcnSent = &opTcnSent + pvstVlanIntf.State.Counters.TcnReceived = &opTcnReceived + } + } else if rpvstVlanIntf != nil && rpvstVlanIntf.State != nil { + rpvstVlanIntf.State.Name = &intfId + rpvstVlanIntf.State.PortNum = &opPortNum + rpvstVlanIntf.State.Cost = &opcost + rpvstVlanIntf.State.PortPriority = &opPortPriority + rpvstVlanIntf.State.DesignatedCost = &opDesigCost + rpvstVlanIntf.State.DesignatedPortNum = &opDesigPortNum + rpvstVlanIntf.State.RootGuardTimer = &opRootGuardTimer + rpvstVlanIntf.State.ForwardTransisitions = &opFwtrans + rpvstVlanIntf.State.DesignatedRootAddress = &desigRootAddr + rpvstVlanIntf.State.DesignatedBridgeAddress = &desigBridgeAddr + switch portState { + case "DISABLED": + rpvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_DISABLED + case "BLOCKING": + rpvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_BLOCKING + case "LISTENING": + rpvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_LISTENING + case "LEARNING": + rpvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_LEARNING + case "FORWARDING": + rpvstVlanIntf.State.PortState = ocbinds.OpenconfigSpanningTreeTypes_STP_PORT_STATE_FORWARDING + } + if rpvstVlanIntf.State.Counters != nil { + rpvstVlanIntf.State.Counters.BpduSent = &opBpduSent + rpvstVlanIntf.State.Counters.BpduReceived = &opBpduReceived + rpvstVlanIntf.State.Counters.TcnSent = &opTcnSent + rpvstVlanIntf.State.Counters.TcnReceived = &opTcnReceived + rpvstVlanIntf.State.Counters.ConfigBpduSent = &opConfigBpduSent + rpvstVlanIntf.State.Counters.ConfigBpduReceived = &opConfigBpduReceived + } + } + } + } else { + vlanIntfOperKeys, _ := app.appDB.GetKeys(app.vlanIntfOperTable) + for i, _ := range vlanIntfOperKeys { + entryKey := vlanIntfOperKeys[i] + if vlanName == (&entryKey).Get(0) { + app.convertOperInternalToOCVlanInterface(vlanName, (&entryKey).Get(1), vlan, vlanIntf) + } + } + } +} + +func (app *StpApp) convertApplDBRpvstVlanToInternal(vlanName string) error { + var err error + + rpvstVlanOperState, err := app.appDB.GetEntry(app.vlanOperTable, asKey(vlanName)) + if err != nil { + return err + } + app.vlanOperTableMap[vlanName] = rpvstVlanOperState + + app.convertApplDBRpvstVlanInterfaceToInternal(vlanName, "") + + return err +} + +func (app *StpApp) convertApplDBRpvstVlanInterfaceToInternal(vlanName string, intfId string) error { + var err error + + if app.vlanIntfOperTableMap[vlanName] == nil { + app.vlanIntfOperTableMap[vlanName] = make(map[string]db.Value) + } + + if len(intfId) > 0 { + rpvstVlanIntfOperState, err := app.appDB.GetEntry(app.vlanIntfOperTable, asKey(vlanName, intfId)) + if err != nil { + return err + } + app.vlanIntfOperTableMap[vlanName][intfId] = rpvstVlanIntfOperState + } else { + vlanIntfOperKeys, _ := app.appDB.GetKeys(app.vlanIntfOperTable) + for i, _ := range vlanIntfOperKeys { + entryKey := vlanIntfOperKeys[i] + if vlanName == (&entryKey).Get(0) { + app.convertApplDBRpvstVlanInterfaceToInternal(vlanName, (&entryKey).Get(1)) + } + } + } + + return err +} + +func (app *StpApp) convertApplDBStpInterfacesToInternal(intfId string) error { + var err error + + intfOperState, err := app.appDB.GetEntry(app.intfOperTable, asKey(intfId)) + if err != nil { + return err + } + app.intfOperTableMap[intfId] = intfOperState + + return err +} + +func (app *StpApp) convertOCStpModeToInternal(mode ocbinds.E_OpenconfigSpanningTreeTypes_STP_PROTOCOL) string { + switch mode { + case ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_MSTP: + return "mstp" + case ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_PVST: + return "pvst" + case ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_RAPID_PVST: + return "rpvst" + case ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_RSTP: + return "rstp" + default: + return "" + } +} + +func (app *StpApp) convertInternalStpModeToOC(mode string) []ocbinds.E_OpenconfigSpanningTreeTypes_STP_PROTOCOL { + var stpModes []ocbinds.E_OpenconfigSpanningTreeTypes_STP_PROTOCOL + if len(mode) > 0 { + switch mode { + case "pvst": + stpModes = append(stpModes, ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_PVST) + case "rpvst": + stpModes = append(stpModes, ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_RAPID_PVST) + case "mstp": + stpModes = append(stpModes, ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_MSTP) + case "rstp": + stpModes = append(stpModes, ocbinds.OpenconfigSpanningTreeTypes_STP_PROTOCOL_RSTP) + } + } + return stpModes +} + +func (app *StpApp) getStpModeFromConfigDB(d *db.DB) (string, error) { + stpGlobalDbEntry, err := d.GetEntry(app.globalTable, asKey("GLOBAL")) + if err != nil { + return "", err + } + return (&stpGlobalDbEntry).Get(STP_MODE), nil +} + +func (app *StpApp) getAllInterfacesFromVlanMemberTable(d *db.DB) ([]string, error) { + var intfList []string + + keys, err := d.GetKeys(&db.TableSpec{Name: "VLAN_MEMBER"}) + if err != nil { + return intfList, err + } + for i, _ := range keys { + key := keys[i] + if !contains(intfList, (&key).Get(1)) { + intfList = append(intfList, (&key).Get(1)) + } + } + return intfList, err +} + +func (app *StpApp) getAllInterfacesFromPortChannelMemberTable(d *db.DB) ([]string, error) { + var intfList []string + + keys, err := d.GetKeys(&db.TableSpec{Name: "PORTCHANNEL_MEMBER"}) + if err != nil { + return intfList, err + } + for i, _ := range keys { + key := keys[i] + if !contains(intfList, (&key).Get(1)) { + intfList = append(intfList, (&key).Get(1)) + } + } + return intfList, err +} + +func (app *StpApp) enableStpForInterfaces(d *db.DB) error { + defaultDBValues := db.Value{Field: map[string]string{}} + (&defaultDBValues).Set("enabled", "true") + (&defaultDBValues).Set("root_guard", "false") + (&defaultDBValues).Set("bpdu_guard", "false") + (&defaultDBValues).Set("bpdu_filter", "global") + (&defaultDBValues).Set("bpdu_guard_do_disable", "false") + (&defaultDBValues).Set("portfast", "true") + (&defaultDBValues).Set("uplink_fast", "false") + + intfList, err := app.getAllInterfacesFromVlanMemberTable(d) + if err != nil { + return err + } + + portKeys, err := d.GetKeys(&db.TableSpec{Name: "PORT"}) + if err != nil { + return err + } + for i, _ := range portKeys { + portKey := portKeys[i] + if contains(intfList, (&portKey).Get(0)) { + d.CreateEntry(app.interfaceTable, portKey, defaultDBValues) + } + } + + // For portchannels + portchKeys, err := d.GetKeys(&db.TableSpec{Name: "PORTCHANNEL"}) + if err != nil { + return err + } + for i, _ := range portchKeys { + portchKey := portchKeys[i] + if contains(intfList, (&portchKey).Get(0)) { + d.CreateEntry(app.interfaceTable, portchKey, defaultDBValues) + } + } + return err +} + +func (app *StpApp) enableStpForVlans(d *db.DB) error { + stpGlobalVal := app.globalInfo + fDelay := (&stpGlobalVal).Get("forward_delay") + helloTime := (&stpGlobalVal).Get("hello_time") + maxAge := (&stpGlobalVal).Get("max_age") + priority := (&stpGlobalVal).Get("priority") + + vlanKeys, err := d.GetKeys(&db.TableSpec{Name: "VLAN"}) + if err != nil { + return err + } + + var vlanList []string + for i, _ := range vlanKeys { + vlanKey := vlanKeys[i] + vlanList = append(vlanList, (&vlanKey).Get(0)) + } + + // Sort vlanList in natural order such that 'Vlan2' < 'Vlan10' + natsort.Sort(vlanList) + + for i, _ := range vlanList { + if i < PVST_MAX_INSTANCES { + defaultDBValues := db.Value{Field: map[string]string{}} + (&defaultDBValues).Set("enabled", "true") + (&defaultDBValues).Set("forward_delay", fDelay) + (&defaultDBValues).Set("hello_time", helloTime) + (&defaultDBValues).Set("max_age", maxAge) + (&defaultDBValues).Set("priority", priority) + + vlanId := strings.Replace(vlanList[i], "Vlan", "", 1) + (&defaultDBValues).Set("vlanid", vlanId) + d.CreateEntry(app.vlanTable, asKey(vlanList[i]), defaultDBValues) + } + } + return err +} + +func enableStpOnVlanCreation(d *db.DB, vlanList []string) { + if len(vlanList) == 0 { + return + } + log.Infof("enableStpOnVlanCreation --> Enable Stp on Vlans: %v", vlanList) + vlanKeys, _ := d.GetKeys(&db.TableSpec{Name: STP_VLAN_TABLE}) + existingEntriesCount := len(vlanKeys) + if existingEntriesCount < PVST_MAX_INSTANCES { + stpGlobalDBEntry, err := d.GetEntry(&db.TableSpec{Name: STP_GLOBAL_TABLE}, asKey("GLOBAL")) + if err != nil { + return + } + fDelay := (&stpGlobalDBEntry).Get("forward_delay") + helloTime := (&stpGlobalDBEntry).Get("hello_time") + maxAge := (&stpGlobalDBEntry).Get("max_age") + priority := (&stpGlobalDBEntry).Get("priority") + + // Sort vlanList in natural order such that 'Vlan2' < 'Vlan10' + natsort.Sort(vlanList) + + for i, _ := range vlanList { + if (existingEntriesCount + i) < PVST_MAX_INSTANCES { + defaultDBValues := db.Value{Field: map[string]string{}} + (&defaultDBValues).Set("enabled", "true") + (&defaultDBValues).Set("forward_delay", fDelay) + (&defaultDBValues).Set("hello_time", helloTime) + (&defaultDBValues).Set("max_age", maxAge) + (&defaultDBValues).Set("priority", priority) + + vlanId := strings.Replace(vlanList[i], "Vlan", "", 1) + (&defaultDBValues).Set("vlanid", vlanId) + d.CreateEntry(&db.TableSpec{Name: STP_VLAN_TABLE}, asKey(vlanList[i]), defaultDBValues) + } + } + } +} + +func removeStpConfigOnVlanDeletion(d *db.DB, vlanList []string) { + if len(vlanList) == 0 { + return + } + log.Infof("removeStpConfigOnVlanDeletion --> Disable Stp on Vlans: %v", vlanList) + for i, _ := range vlanList { + err := d.DeleteEntry(&db.TableSpec{Name: STP_VLAN_INTF_TABLE}, asKey(vlanList[i], "*")) + if err != nil { + log.Error(err) + } + err = d.DeleteEntry(&db.TableSpec{Name: STP_VLAN_TABLE}, asKey(vlanList[i])) + if err != nil { + log.Error(err) + } + } +} + +// This function accepts list of Interface names (i.e. Eth or Portchannel) +// on which switchport is enabled +func enableStpOnInterfaceVlanMembership(d *db.DB, intfList []string) { + if len(intfList) == 0 { + return + } + _, serr := d.GetEntry(&db.TableSpec{Name: STP_GLOBAL_TABLE}, asKey("GLOBAL")) + if serr != nil { + return + } + log.Infof("enableStpOnInterfaceVlanMembership --> Enable Stp on Interfaces: %v", intfList) + defaultDBValues := db.Value{Field: map[string]string{}} + (&defaultDBValues).Set("enabled", "true") + (&defaultDBValues).Set("root_guard", "false") + (&defaultDBValues).Set("bpdu_guard", "false") + (&defaultDBValues).Set("bpdu_filter", "global") + (&defaultDBValues).Set("bpdu_guard_do_disable", "false") + (&defaultDBValues).Set("portfast", "true") + (&defaultDBValues).Set("uplink_fast", "false") + + var stpEnabledIntfList []string + intfKeys, err := d.GetKeys(&db.TableSpec{Name: STP_INTF_TABLE}) + if err != nil { + log.Error(err) + } else { + for i, _ := range intfKeys { + dbKey := intfKeys[i] + stpEnabledIntfList = append(stpEnabledIntfList, (&dbKey).Get(0)) + } + + for i, _ := range intfList { + if !contains(stpEnabledIntfList, intfList[i]) { + d.CreateEntry(&db.TableSpec{Name: STP_INTF_TABLE}, asKey(intfList[i]), defaultDBValues) + } + } + } +} + +// This function accepts list of Interface names (i.e. Eth or Portchannel) +// on which switchport is disabled +func removeStpOnInterfaceSwitchportDeletion(d *db.DB, intfList []string) { + if len(intfList) == 0 { + return + } + log.Infof("removeStpOnInterfaceSwitchportDeletion --> Disable Stp on Interfaces: %v", intfList) + for i, _ := range intfList { + err := d.DeleteEntry(&db.TableSpec{Name: STP_VLAN_INTF_TABLE}, asKey("*", intfList[i])) + if err != nil { + log.Error(err) + } + err = d.DeleteEntry(&db.TableSpec{Name: STP_INTF_TABLE}, asKey(intfList[i])) + if err != nil { + log.Error(err) + } + } +} + +func (app *StpApp) updateGlobalFieldsToStpVlanTable(d *db.DB, fldValuePair map[string]string, stpGlobalDbEntry db.Value) error { + vlanKeys, err := d.GetKeys(app.vlanTable) + if err != nil { + return err + } + for i, _ := range vlanKeys { + vlanEntry, _ := d.GetEntry(app.vlanTable, vlanKeys[i]) + + for fldName := range fldValuePair { + valStr := fldValuePair[fldName] + globalFldVal := (&stpGlobalDbEntry).Get(fldName) + + if (&vlanEntry).Get(fldName) == globalFldVal { + (&vlanEntry).Set(fldName, valStr) + err := d.ModEntry(app.vlanTable, vlanKeys[i], vlanEntry) + if err != nil { + return err + } + } + } + } + return nil +} + +func (app *StpApp) enableStpMode(d *db.DB) error { + var err error + err = app.setStpGlobalConfigInDB(d) + if err != nil { + return err + } + err = app.enableStpForInterfaces(d) + if err != nil { + return err + } + err = app.enableStpForVlans(d) + + return err +} + +func (app *StpApp) disableStpMode(d *db.DB) error { + var err error + err = d.DeleteTable(app.vlanIntfTable) + if err != nil { + return err + } + err = d.DeleteTable(app.vlanTable) + if err != nil { + return err + } + err = d.DeleteTable(app.interfaceTable) + if err != nil { + return err + } + err = d.DeleteTable(app.globalTable) + + return err +} + +func (app *StpApp) handleStpGlobalFieldsUpdation(d *db.DB, opcode int) error { + stpGlobalDBEntry, err := d.GetEntry(app.globalTable, asKey("GLOBAL")) + if err != nil { + return err + } + // Make a copy of StpGlobalDBEntry to modify fields value. + tmpDbEntry := db.Value{Field: map[string]string{}} + for field, value := range stpGlobalDBEntry.Field { + tmpDbEntry.Field[field] = value + } + fldValuePair := make(map[string]string) + + for fld := range app.globalInfo.Field { + valStr := app.globalInfo.Field[fld] + (&tmpDbEntry).Set(fld, valStr) + + if fld != "rootguard_timeout" && fld != "bpdu_filter" { + fldValuePair[fld] = valStr + } + } + + err = d.ModEntry(app.globalTable, asKey("GLOBAL"), tmpDbEntry) + if err != nil { + return err + } + + err = app.updateGlobalFieldsToStpVlanTable(d, fldValuePair, stpGlobalDBEntry) + if err != nil { + return err + } + + return nil +} + +func (app *StpApp) handleStpGlobalFieldsDeletion(d *db.DB) error { + stpGlobalDBEntry, err := d.GetEntry(app.globalTable, asKey("GLOBAL")) + if err != nil { + return err + } + + nodeInfo, err := getTargetNodeYangSchema(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device)) + if err != nil { + return err + } + log.Infof("Node received for deletion: %s", nodeInfo.Name) + if nodeInfo.IsLeaf() { + var fldName, valStr string + switch nodeInfo.Name { + case "rootguard-timeout": + fldName = "rootguard_timeout" + valStr = STP_DEFAULT_ROOT_GUARD_TIMEOUT + case "hello-time": + fldName = "hello_time" + valStr = STP_DEFAULT_HELLO_INTERVAL + case "max-age": + fldName = "max_age" + valStr = STP_DEFAULT_MAX_AGE + case "forwarding-delay": + fldName = "forward_delay" + valStr = STP_DEFAULT_FORWARD_DELAY + case "bridge-priority": + fldName = "priority" + valStr = STP_DEFAULT_BRIDGE_PRIORITY + case "bpdu-filter": + fldName = "bpdu_filter" + valStr = STP_DEFAULT_BPDU_FILTER + } + + // Make a copy of StpGlobalDBEntry to modify fields value. + tmpDbEntry := db.Value{Field: map[string]string{}} + for field, value := range stpGlobalDBEntry.Field { + tmpDbEntry.Field[field] = value + } + fldValuePair := make(map[string]string) + + (&tmpDbEntry).Set(fldName, valStr) + err = d.ModEntry(app.globalTable, asKey("GLOBAL"), tmpDbEntry) + if err != nil { + return err + } + + if fldName != "rootguard_timeout" && fldName != "bpdu_filter" { + fldValuePair[fldName] = valStr + } + + err = app.updateGlobalFieldsToStpVlanTable(d, fldValuePair, stpGlobalDBEntry) + if err != nil { + return err + } + } + return nil +} + +func (app *StpApp) handleVlanInterfaceFieldsDeletion(d *db.DB, vlanName string, intfId string) error { + dbEntry, err := d.GetEntry(app.vlanIntfTable, asKey(vlanName, intfId)) + if err != nil { + return err + } + + nodeInfo, err := getTargetNodeYangSchema(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device)) + if err != nil { + return err + } + log.Infof("Node received for deletion: %s", nodeInfo.Name) + if nodeInfo.IsLeaf() { + switch nodeInfo.Name { + case "cost": + (&dbEntry).Remove("path_cost") + case "port-priority": + (&dbEntry).Remove("priority") + } + } + + err = d.SetEntry(app.vlanIntfTable, asKey(vlanName, intfId), dbEntry) + if err != nil { + return err + } + + return nil +} + +func (app *StpApp) handleVlanFieldsDeletion(d *db.DB, vlanName string) error { + stpGlobalDBEntry, err := d.GetEntry(app.globalTable, asKey("GLOBAL")) + if err != nil { + return err + } + fDelay := (&stpGlobalDBEntry).Get("forward_delay") + helloTime := (&stpGlobalDBEntry).Get("hello_time") + maxAge := (&stpGlobalDBEntry).Get("max_age") + priority := (&stpGlobalDBEntry).Get("priority") + + dbEntry, err := d.GetEntry(app.vlanTable, asKey(vlanName)) + if err != nil { + return err + } + + nodeInfo, err := getTargetNodeYangSchema(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device)) + if err != nil { + return err + } + log.Infof("Node received for deletion: %s", nodeInfo.Name) + if nodeInfo.IsLeaf() { + switch nodeInfo.Name { + case "hello-time": + (&dbEntry).Set("hello_time", helloTime) + case "max-age": + (&dbEntry).Set("max_age", maxAge) + case "bridge-priority": + (&dbEntry).Set("priority", priority) + case "forwarding-delay": + (&dbEntry).Set("forward_delay", fDelay) + case "spanning-tree-enable": + (&dbEntry).Set("enabled", "false") + } + } + + err = d.ModEntry(app.vlanTable, asKey(vlanName), dbEntry) + if err != nil { + return err + } + + return nil +} + +func (app *StpApp) handleInterfacesFieldsDeletion(d *db.DB, intfId string) error { + dbEntry, err := d.GetEntry(app.interfaceTable, asKey(intfId)) + if err != nil { + return err + } + + nodeInfo, err := getTargetNodeYangSchema(app.pathInfo.Path, (*app.ygotRoot).(*ocbinds.Device)) + if err != nil { + return err + } + log.Infof("Node received for deletion: %s", nodeInfo.Name) + if nodeInfo.IsLeaf() { + switch nodeInfo.Name { + case "guard": + (&dbEntry).Remove("root_guard") + case "bpdu-guard": + (&dbEntry).Remove("bpdu_guard") + case "bpdu-filter": + (&dbEntry).Set("bpdu_filter", "global") + case "portfast": + (&dbEntry).Remove("portfast") + case "uplink-fast": + (&dbEntry).Remove("uplink_fast") + case "bpdu-guard-port-shutdown": + (&dbEntry).Remove("bpdu_guard_do_disable") + case "cost": + (&dbEntry).Remove("path_cost") + case "port-priority": + (&dbEntry).Remove("priority") + case "spanning-tree-enable": + (&dbEntry).Remove("enabled") + case "edge-port": + (&dbEntry).Remove("edge_port") + case "link-type": + (&dbEntry).Set("link_type", "auto") + } + } + + err = d.SetEntry(app.interfaceTable, asKey(intfId), dbEntry) + if err != nil { + return err + } + + return nil +} diff --git a/src/translib/stp_app_test.go b/src/translib/stp_app_test.go new file mode 100644 index 0000000000..8f38f13c23 --- /dev/null +++ b/src/translib/stp_app_test.go @@ -0,0 +1,201 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "errors" + "fmt" + "io/ioutil" + "testing" + db "translib/db" +) + +func init() { + fmt.Println("+++++ Init stp_app_test +++++") + + if err := clearStpDataFromConfigDb(); err == nil { + fmt.Println("+++++ Removed All Stp Data from Db +++++") + } else { + fmt.Printf("Failed to remove All Stp Data from Db: %v", err) + } +} + +// This will Test PVST mode enable/disable +func Test_StpApp_Pvst_Enable_Disable(t *testing.T) { + topStpUrl := "/openconfig-spanning-tree:stp" + enableStpUrl := topStpUrl + "/global" + + t.Run("Empty_Response_Top_Level", processGetRequest(topStpUrl, "", true)) + + t.Run("Enable_PVST_Mode", processSetRequest(enableStpUrl, enablePVSTModeJsonRequest, "POST", false)) + t.Run("Verify_PVST_Mode_Configured", processGetRequest(enableStpUrl+"/state", pvstmodeVerifyJsonResponse, false)) + t.Run("Disable_PVST_Mode", processDeleteRequest(topStpUrl)) + + t.Run("Verify_Empty_Response_Top_Level", processGetRequest(topStpUrl, "", true)) +} + +// This will Test RAPID PVST mode enable/disable +func Test_StpApp_Rapid_Pvst_Enable_Disable(t *testing.T) { + topStpUrl := "/openconfig-spanning-tree:stp" + enableStpUrl := topStpUrl + "/global" + + t.Run("Empty_Response_Top_Level", processGetRequest(topStpUrl, "", true)) + + t.Run("Enable_Rapid_PVST_Mode", processSetRequest(enableStpUrl, enableRapidPVSTModeJsonRequest, "POST", false)) + t.Run("Verify_Rapid_PVST_Mode_Configured", processGetRequest(enableStpUrl+"/state", rapidPvstmodeVerifyJsonResponse, false)) + t.Run("Disable_Rapid_PVST_Mode", processDeleteRequest(topStpUrl)) + + t.Run("Verify_Empty_Response_Top_Level", processGetRequest(topStpUrl, "", true)) +} + +func Test_StpApp_TopLevelPathInPvstMode(t *testing.T) { + topStpUrl := "/openconfig-spanning-tree:stp" + enableStpUrl := topStpUrl + "/global" + vlanUrl := "/openconfig-interfaces:interfaces/interface[name=Vlan4090]" + createswitchportUrl := "/openconfig-interfaces:interfaces/interface[name=Ethernet28]/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + deleteSwitchportUrl := createswitchportUrl + "/trunk-vlans[trunk-vlans=4090]" + + t.Run("Empty_Response_Top_Level", processGetRequest(topStpUrl, "", true)) + + t.Run("Create_Single_Vlan", processSetRequest(vlanUrl, emptyJson, "PATCH", false)) + t.Run("Create_Switchport", processSetRequest(createswitchportUrl, switchportCreateJsonRequest, "PATCH", false)) + + t.Run("Enable_PVST_Mode", processSetRequest(enableStpUrl, enablePVSTModeJsonRequest, "POST", false)) + t.Run("Verify_Full_Stp_Top_Level", processGetRequest(topStpUrl, topLevelPvstModeVerifyJsonResponse, false)) + t.Run("Disable_PVST_Mode", processDeleteRequest(topStpUrl)) + + t.Run("Delete_Switchport", processDeleteRequest(deleteSwitchportUrl)) + t.Run("Delete_Single_Vlan", processDeleteRequest(vlanUrl)) + + t.Run("Verify_Disable_PVST_Mode", processGetRequest(topStpUrl, "", true)) +} + +func Test_StpApp_TopLevelPathInRapidPvstMode(t *testing.T) { + topStpUrl := "/openconfig-spanning-tree:stp" + enableStpUrl := topStpUrl + "/global" + vlanUrl := "/openconfig-interfaces:interfaces/interface[name=Vlan4090]" + createswitchportUrl := "/openconfig-interfaces:interfaces/interface[name=Ethernet28]/openconfig-if-ethernet:ethernet/openconfig-vlan:switched-vlan/config" + deleteSwitchportUrl := createswitchportUrl + "/trunk-vlans[trunk-vlans=4090]" + + t.Run("Empty_Response_Top_Level", processGetRequest(topStpUrl, "", true)) + + t.Run("Create_Single_Vlan", processSetRequest(vlanUrl, emptyJson, "PATCH", false)) + t.Run("Create_Switchport", processSetRequest(createswitchportUrl, switchportCreateJsonRequest, "PATCH", false)) + + t.Run("Enable_Rapid_PVST_Mode", processSetRequest(enableStpUrl, enableRapidPVSTModeJsonRequest, "POST", false)) + t.Run("Verify_Full_Stp_Top_Level", processGetRequest(topStpUrl, topLevelRapidPvstModeVerifyJsonResponse, false)) + t.Run("Disable_Rapid_PVST_Mode", processDeleteRequest(topStpUrl)) + + t.Run("Delete_Switchport", processDeleteRequest(deleteSwitchportUrl)) + t.Run("Delete_Single_Vlan", processDeleteRequest(vlanUrl)) + + t.Run("Verify_Disable_Rapid_PVST_Mode", processGetRequest(topStpUrl, "", true)) +} + +func clearStpDataFromConfigDb() error { + var err error + stpGlobalTbl := db.TableSpec{Name: "STP"} + stpVlanTbl := db.TableSpec{Name: "STP_VLAN"} + stpVlanIntfTbl := db.TableSpec{Name: "STP_VLAN_INTF"} + stpIntfTbl := db.TableSpec{Name: "STP_INTF"} + + d := getConfigDb() + if d == nil { + err = errors.New("Failed to connect to config Db") + return err + } + + if err = d.DeleteTable(&stpVlanIntfTbl); err != nil { + err = errors.New("Failed to delete STP Vlan Intf Table") + return err + } + + if err = d.DeleteTable(&stpIntfTbl); err != nil { + err = errors.New("Failed to delete STP Intf Table") + return err + } + + if err = d.DeleteTable(&stpVlanTbl); err != nil { + err = errors.New("Failed to delete STP Vlan Table") + return err + } + + if err = d.DeleteTable(&stpGlobalTbl); err != nil { + err = errors.New("Failed to delete STP Global Table") + return err + } + + /* Temporary + if err = d.DeleteTable(&db.TableSpec{Name: "PORTCHANNEL_MEMBER"}); err != nil { + err = errors.New("Failed to delete PORTCHANNEL_MEMBER Table") + return err + } + if err = d.DeleteTable(&db.TableSpec{Name: "PORTCHANNEL"}); err != nil { + err = errors.New("Failed to delete PORTCHANNEL Table") + return err + } + if err = d.DeleteTable(&db.TableSpec{Name: "VLAN_MEMBER"}); err != nil { + err = errors.New("Failed to delete VLAN_MEMBER Table") + return err + } + if err = d.DeleteTable(&db.TableSpec{Name: "VLAN"}); err != nil { + err = errors.New("Failed to delete VLAN Table") + return err + } + */ + + return err +} + +func processGetRequestToFile(url string, expectedRespJson string, errorCase bool) func(*testing.T) { + return func(t *testing.T) { + response, err := Get(GetRequest{Path:url}) + if err != nil && !errorCase { + t.Errorf("Error %v received for Url: %s", err, url) + } + + respJson := response.Payload + err = ioutil.WriteFile("/tmp/TmpResp.json", respJson, 0644) + if err != nil { + fmt.Println(err) + } + if string(respJson) != expectedRespJson { + t.Errorf("Response for Url: %s received is not expected:\n%s", url, string(respJson)) + } + } +} + +/***************************************************************************/ +/////////// JSON Data for Tests /////////////// +/***************************************************************************/ + +var switchportCreateJsonRequest string = "{\"openconfig-vlan:config\": {\"trunk-vlans\": [4090], \"interface-mode\": \"TRUNK\"}}" + +var enablePVSTModeJsonRequest string = "{ \"openconfig-spanning-tree:config\": { \"enabled-protocol\": [ \"PVST\" ], \"openconfig-spanning-tree-ext:rootguard-timeout\": 401, \"openconfig-spanning-tree-ext:hello-time\": 7, \"openconfig-spanning-tree-ext:max-age\": 16, \"openconfig-spanning-tree-ext:forwarding-delay\": 22, \"openconfig-spanning-tree-ext:bridge-priority\": 20480 }}" + +var pvstmodeVerifyJsonResponse string = "{\"openconfig-spanning-tree:state\":{\"bpdu-filter\":false,\"enabled-protocol\":[\"openconfig-spanning-tree-ext:PVST\"],\"openconfig-spanning-tree-ext:bridge-priority\":20480,\"openconfig-spanning-tree-ext:forwarding-delay\":22,\"openconfig-spanning-tree-ext:hello-time\":7,\"openconfig-spanning-tree-ext:max-age\":16,\"openconfig-spanning-tree-ext:rootguard-timeout\":401}}" + +var topLevelPvstModeVerifyJsonResponse string = "{\"openconfig-spanning-tree:stp\":{\"global\":{\"config\":{\"bpdu-filter\":false,\"enabled-protocol\":[\"openconfig-spanning-tree-ext:PVST\"],\"openconfig-spanning-tree-ext:bridge-priority\":20480,\"openconfig-spanning-tree-ext:forwarding-delay\":22,\"openconfig-spanning-tree-ext:hello-time\":7,\"openconfig-spanning-tree-ext:max-age\":16,\"openconfig-spanning-tree-ext:rootguard-timeout\":401},\"state\":{\"bpdu-filter\":false,\"enabled-protocol\":[\"openconfig-spanning-tree-ext:PVST\"],\"openconfig-spanning-tree-ext:bridge-priority\":20480,\"openconfig-spanning-tree-ext:forwarding-delay\":22,\"openconfig-spanning-tree-ext:hello-time\":7,\"openconfig-spanning-tree-ext:max-age\":16,\"openconfig-spanning-tree-ext:rootguard-timeout\":401}},\"interfaces\":{\"interface\":[{\"config\":{\"bpdu-guard\":false,\"guard\":\"NONE\",\"name\":\"Ethernet28\",\"openconfig-spanning-tree-ext:bpdu-guard-port-shutdown\":false,\"openconfig-spanning-tree-ext:portfast\":true,\"openconfig-spanning-tree-ext:spanning-tree-enable\":true,\"openconfig-spanning-tree-ext:uplink-fast\":false},\"name\":\"Ethernet28\",\"state\":{\"bpdu-guard\":false,\"guard\":\"NONE\",\"name\":\"Ethernet28\",\"openconfig-spanning-tree-ext:bpdu-guard-port-shutdown\":false,\"openconfig-spanning-tree-ext:spanning-tree-enable\":true,\"openconfig-spanning-tree-ext:uplink-fast\":false}}]},\"openconfig-spanning-tree-ext:pvst\":{\"vlan\":[{\"config\":{\"bridge-priority\":20480,\"forwarding-delay\":22,\"hello-time\":7,\"max-age\":16,\"spanning-tree-enable\":true,\"vlan-id\":4090},\"state\":{\"bridge-priority\":20480,\"vlan-id\":4090},\"vlan-id\":4090}]}}}" + +var enableRapidPVSTModeJsonRequest string = "{ \"openconfig-spanning-tree:config\": { \"enabled-protocol\": [ \"RAPID_PVST\" ], \"openconfig-spanning-tree-ext:rootguard-timeout\": 305, \"openconfig-spanning-tree-ext:hello-time\": 4, \"openconfig-spanning-tree-ext:max-age\": 10, \"openconfig-spanning-tree-ext:forwarding-delay\": 25, \"openconfig-spanning-tree-ext:bridge-priority\": 20480 }}" + +var rapidPvstmodeVerifyJsonResponse string = "{\"openconfig-spanning-tree:state\":{\"bpdu-filter\":false,\"enabled-protocol\":[\"openconfig-spanning-tree-types:RAPID_PVST\"],\"openconfig-spanning-tree-ext:bridge-priority\":20480,\"openconfig-spanning-tree-ext:forwarding-delay\":25,\"openconfig-spanning-tree-ext:hello-time\":4,\"openconfig-spanning-tree-ext:max-age\":10,\"openconfig-spanning-tree-ext:rootguard-timeout\":305}}" + +var topLevelRapidPvstModeVerifyJsonResponse string = "{\"openconfig-spanning-tree:stp\":{\"global\":{\"config\":{\"bpdu-filter\":false,\"enabled-protocol\":[\"openconfig-spanning-tree-types:RAPID_PVST\"],\"openconfig-spanning-tree-ext:bridge-priority\":20480,\"openconfig-spanning-tree-ext:forwarding-delay\":25,\"openconfig-spanning-tree-ext:hello-time\":4,\"openconfig-spanning-tree-ext:max-age\":10,\"openconfig-spanning-tree-ext:rootguard-timeout\":305},\"state\":{\"bpdu-filter\":false,\"enabled-protocol\":[\"openconfig-spanning-tree-types:RAPID_PVST\"],\"openconfig-spanning-tree-ext:bridge-priority\":20480,\"openconfig-spanning-tree-ext:forwarding-delay\":25,\"openconfig-spanning-tree-ext:hello-time\":4,\"openconfig-spanning-tree-ext:max-age\":10,\"openconfig-spanning-tree-ext:rootguard-timeout\":305}},\"interfaces\":{\"interface\":[{\"config\":{\"bpdu-guard\":false,\"guard\":\"NONE\",\"name\":\"Ethernet28\",\"openconfig-spanning-tree-ext:bpdu-guard-port-shutdown\":false,\"openconfig-spanning-tree-ext:portfast\":true,\"openconfig-spanning-tree-ext:spanning-tree-enable\":true,\"openconfig-spanning-tree-ext:uplink-fast\":false},\"name\":\"Ethernet28\",\"state\":{\"bpdu-guard\":false,\"guard\":\"NONE\",\"name\":\"Ethernet28\",\"openconfig-spanning-tree-ext:bpdu-guard-port-shutdown\":false,\"openconfig-spanning-tree-ext:spanning-tree-enable\":true,\"openconfig-spanning-tree-ext:uplink-fast\":false}}]},\"rapid-pvst\":{\"vlan\":[{\"config\":{\"bridge-priority\":20480,\"forwarding-delay\":25,\"hello-time\":4,\"max-age\":10,\"openconfig-spanning-tree-ext:spanning-tree-enable\":true,\"vlan-id\":4090},\"state\":{\"bridge-priority\":20480,\"vlan-id\":4090},\"vlan-id\":4090}]}}}" diff --git a/src/translib/subscribe.go b/src/translib/subscribe.go new file mode 100644 index 0000000000..9baf69070a --- /dev/null +++ b/src/translib/subscribe.go @@ -0,0 +1,341 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 + +handler to subscribe for a key space notification. It also has + +functions to handle the key space notification from redis and + +call the appropriate app module to handle them. + +*/ + +package translib + +import ( + "sync" + "time" + "bytes" + "strconv" + "translib/db" + log "github.com/golang/glog" + "github.com/Workiva/go-datastructures/queue" +) + +//Subscribe mutex for all the subscribe operations on the maps to be thread safe +var sMutex = &sync.Mutex{} + +type notificationInfo struct{ + table db.TableSpec + key db.Key + dbno db.DBNum + needCache bool + path string + app *appInterface + appInfo *appInfo + cache []byte + sKey *db.SKey + dbs [db.MaxDB] *db.DB //used to perform get operations +} + +type subscribeInfo struct{ + syncDone bool + q *queue.PriorityQueue + nInfoArr []*notificationInfo + stop chan struct{} + sDBs []*db.DB //Subscription DB should be used only for keyspace notification unsubscription +} + +var nMap map[*db.SKey]*notificationInfo +var sMap map[*notificationInfo]*subscribeInfo +var stopMap map[chan struct{}]*subscribeInfo +var cleanupMap map[*db.DB]*subscribeInfo + +func init() { + nMap = make(map[*db.SKey]*notificationInfo) + sMap = make(map[*notificationInfo]*subscribeInfo) + stopMap = make(map[chan struct{}]*subscribeInfo) + cleanupMap = make(map[*db.DB]*subscribeInfo) +} + +func runSubscribe(q *queue.PriorityQueue) error { + var err error + + for i := 0; i < 10; i++ { + time.Sleep(2 * time.Second) + q.Put(&SubscribeResponse{ + Path:"/testPath", + Payload:[]byte("test payload"), + Timestamp: time.Now().UnixNano(), + }) + + } + + return err +} + +func startDBSubscribe(opt db.Options, nInfoList []*notificationInfo, sInfo *subscribeInfo) error { + var sKeyList []*db.SKey + + for _, nInfo := range nInfoList { + sKey := &db.SKey{ Ts: &nInfo.table, Key: &nInfo.key} + sKeyList = append(sKeyList, sKey) + nInfo.sKey = sKey + nMap[sKey] = nInfo + sMap[nInfo] = sInfo + } + + sDB, err := db.SubscribeDB(opt, sKeyList, notificationHandler) + + if err == nil { + sInfo.sDBs = append(sInfo.sDBs, sDB) + cleanupMap[sDB] = sInfo + } else { + for i, nInfo := range nInfoList { + delete(nMap, sKeyList[i]) + delete(sMap, nInfo) + } + } + + return err +} + +func notificationHandler(d *db.DB, sKey *db.SKey, key *db.Key, event db.SEvent) error { + log.Info("notificationHandler: d: ", d, " sKey: ", *sKey, " key: ", *key, + " event: ", event) + switch event { + case db.SEventHSet, db.SEventHDel, db.SEventDel: + sMutex.Lock() + defer sMutex.Unlock() + + if sKey != nil { + if nInfo, ok := nMap[sKey]; (ok && nInfo != nil) { + if sInfo, ok := sMap[nInfo]; (ok && sInfo != nil) { + isChanged := isCacheChanged(nInfo) + + if isChanged { + sendNotification(sInfo, nInfo, false) + } + } else { + log.Info("sInfo not in map", sInfo) + } + } else { + log.Info("nInfo not in map", nInfo) + } + } + case db.SEventClose: + case db.SEventErr: + if sInfo, ok := cleanupMap[d]; (ok && sInfo != nil) { + nInfo := sInfo.nInfoArr[0] + if nInfo != nil { + sendNotification(sInfo, nInfo, true) + } + } + } + + return nil +} + +func updateCache(nInfo *notificationInfo) error { + var err error + + json, err1 := getJson (nInfo) + + if err1 == nil { + nInfo.cache = json + } else { + log.Error("Failed to get the Json for the path = ", nInfo.path) + log.Error("Error returned = ", err1) + + nInfo.cache = []byte("{}") + } + + return err +} + +func isCacheChanged(nInfo *notificationInfo) bool { + json, err := getJson (nInfo) + + if err != nil { + json = []byte("{}") + } + + if bytes.Equal(nInfo.cache, json) { + log.Info("Cache is same as DB") + return false + } else { + log.Info("Cache is NOT same as DB") + nInfo.cache = json + return true + } + + return false +} + +func startSubscribe(sInfo *subscribeInfo, dbNotificationMap map[db.DBNum][]*notificationInfo) error { + var err error + + sMutex.Lock() + defer sMutex.Unlock() + + stopMap[sInfo.stop] = sInfo + + for dbno, nInfoArr := range dbNotificationMap { + isWriteDisabled := true + opt := getDBOptions(dbno, isWriteDisabled) + err = startDBSubscribe(opt, nInfoArr, sInfo) + + if err != nil { + cleanup (sInfo.stop) + return err + } + + sInfo.nInfoArr = append(sInfo.nInfoArr, nInfoArr...) + } + + for i, nInfo := range sInfo.nInfoArr { + err = updateCache(nInfo) + + if err != nil { + cleanup (sInfo.stop) + return err + } + + if i == len(sInfo.nInfoArr)-1 { + sInfo.syncDone = true + } + + sendNotification(sInfo, nInfo, false) + } + //printAllMaps() + + go stophandler(sInfo.stop) + + return err +} + +func getJson (nInfo *notificationInfo) ([]byte, error) { + var payload []byte + + app := nInfo.app + path := nInfo.path + appInfo := nInfo.appInfo + + err := appInitialize(app, appInfo, path, nil, GET) + + if err != nil { + return payload, err + } + + dbs := nInfo.dbs + + err = (*app).translateGet (dbs) + + if err != nil { + return payload, err + } + + resp, err := (*app).processGet(dbs) + + if err == nil { + payload = resp.Payload + } + + return payload, err +} + +func sendNotification(sInfo *subscribeInfo, nInfo *notificationInfo, isTerminated bool){ + log.Info("Sending notification for sInfo = ", sInfo) + log.Info("payload = ", string(nInfo.cache)) + log.Info("isTerminated", strconv.FormatBool(isTerminated)) + sInfo.q.Put(&SubscribeResponse{ + Path:nInfo.path, + Payload:nInfo.cache, + Timestamp: time.Now().UnixNano(), + SyncComplete: sInfo.syncDone, + IsTerminated: isTerminated, + }) +} + +func stophandler(stop chan struct{}) { + for { + select { + case <-stop: + log.Info("stop channel signalled") + sMutex.Lock() + defer sMutex.Unlock() + + cleanup (stop) + + return + } + } + + return +} + +func cleanup(stop chan struct{}) { + if sInfo,ok := stopMap[stop]; ok { + + for _, sDB := range sInfo.sDBs { + sDB.UnsubscribeDB() + } + + for _, nInfo := range sInfo.nInfoArr { + delete(nMap, nInfo.sKey) + delete(sMap, nInfo) + } + + delete(stopMap, stop) + } + //printAllMaps() +} + +//Debugging functions +func printnMap() { + log.Info("Printing the contents of nMap") + for sKey, nInfo := range nMap { + log.Info("sKey = ", sKey) + log.Info("nInfo = ", nInfo) + } +} + +func printStopMap() { + log.Info("Printing the contents of stopMap") + for stop, sInfo := range stopMap { + log.Info("stop = ", stop) + log.Info("sInfo = ", sInfo) + } +} + +func printsMap() { + log.Info("Printing the contents of sMap") + for sInfo, nInfo := range sMap { + log.Info("nInfo = ", nInfo) + log.Info("sKey = ", sInfo) + } +} + +func printAllMaps() { + printnMap() + printsMap() + printStopMap() +} 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/acl/01_create_MyACL1_MyACL2.json b/src/translib/test/acl/01_create_MyACL1_MyACL2.json new file mode 100644 index 0000000000..be3becbd44 --- /dev/null +++ b/src/translib/test/acl/01_create_MyACL1_MyACL2.json @@ -0,0 +1,343 @@ +{ + "acl-sets": { + "acl-set": [ + { + "name": "MyACL1", + "type": "ACL_IPV4", + "config": { + "name": "MyACL1", + "type": "ACL_IPV4", + "description": "Description for MyACL1" + }, + "acl-entries": { + "acl-entry": [ + { + "sequence-id": 1, + "config": { + "sequence-id": 1, + "description": "Description for MyACL1 Rule Seq 1" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.1/32", + "destination-address": "21.1.1.1/32", + "dscp": 1, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 101, + "destination-port": 201 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 2, + "config": { + "sequence-id": 2, + "description": "Description for MyACL1 Rule Seq 2" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.2/32", + "destination-address": "21.1.1.2/32", + "dscp": 2, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 102, + "destination-port": 202 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 3, + "config": { + "sequence-id": 3, + "description": "Description for MyACL1 Rule Seq 3" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.3/32", + "destination-address": "21.1.1.3/32", + "dscp": 3, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 103, + "destination-port": 203 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 4, + "config": { + "sequence-id": 4, + "description": "Description for MyACL1 Rule Seq 4" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.4/32", + "destination-address": "21.1.1.4/32", + "dscp": 4, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 104, + "destination-port": 204 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 5, + "config": { + "sequence-id": 5, + "description": "Description for MyACL1 Rule Seq 5" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.5/32", + "destination-address": "21.1.1.5/32", + "dscp": 5, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 105, + "destination-port": 205 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + } + ] + } + }, + { + "name": "MyACL2", + "type": "ACL_IPV4", + "config": { + "name": "MyACL2", + "type": "ACL_IPV4", + "description": "Description for MyACL2" + }, + "acl-entries": { + "acl-entry": [ + { + "sequence-id": 1, + "config": { + "sequence-id": 1, + "description": "Description for Rule Seq 1" + }, + "ipv4": { + "config": { + "source-address": "12.1.1.1/32", + "destination-address": "22.1.1.1/32", + "dscp": 1, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 101, + "destination-port": 201 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 2, + "config": { + "sequence-id": 2, + "description": "Description for Rule Seq 2" + }, + "ipv4": { + "config": { + "source-address": "12.1.1.2/32", + "destination-address": "22.1.1.2/32", + "dscp": 2, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 102, + "destination-port": 202 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 3, + "config": { + "sequence-id": 3, + "description": "Description for Rule Seq 3" + }, + "ipv4": { + "config": { + "source-address": "12.1.1.3/32", + "destination-address": "22.1.1.3/32", + "dscp": 3, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 103, + "destination-port": 203 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 4, + "config": { + "sequence-id": 4, + "description": "Description for Rule Seq 4" + }, + "ipv4": { + "config": { + "source-address": "12.1.1.4/32", + "destination-address": "22.1.1.4/32", + "dscp": 4, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 104, + "destination-port": 204 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 5, + "config": { + "sequence-id": 5, + "description": "Description for Rule Seq 5" + }, + "ipv4": { + "config": { + "source-address": "12.1.1.5/32", + "destination-address": "22.1.1.5/32", + "dscp": 5, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 105, + "destination-port": 205 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + } + ] + } + } + ] + }, + "interfaces": { + "interface": [ + { + "id": "Ethernet0", + "config": { + "id": "Ethernet0" + }, + "interface-ref": { + "config": { + "interface": "Ethernet0" + } + }, + "ingress-acl-sets": { + "ingress-acl-set": [ + { + "set-name": "MyACL1", + "type": "ACL_IPV4", + "config": { + "set-name": "MyACL1", + "type": "ACL_IPV4" + } + } + ] + } + }, + { + "id": "Ethernet4", + "config": { + "id": "Ethernet4" + }, + "interface-ref": { + "config": { + "interface": "Ethernet4" + } + }, + "ingress-acl-sets": { + "ingress-acl-set": [ + { + "set-name": "MyACL2", + "type": "ACL_IPV4", + "config": { + "set-name": "MyACL2", + "type": "ACL_IPV4" + } + } + ] + } + } + ] + } +} + diff --git a/src/translib/test/acl/02_create_MyACL3_5Rules.json b/src/translib/test/acl/02_create_MyACL3_5Rules.json new file mode 100644 index 0000000000..5fbadafad3 --- /dev/null +++ b/src/translib/test/acl/02_create_MyACL3_5Rules.json @@ -0,0 +1,147 @@ +{ + "acl-set": [ + { + "name": "MyACL3", + "type": "ACL_IPV4", + "config": { + "name": "MyACL3", + "type": "ACL_IPV4", + "description": "Description for MyACL3" + }, + "acl-entries": { + "acl-entry": [ + { + "sequence-id": 1, + "config": { + "sequence-id": 1, + "description": "Description for MyACL3 Rule Seq 1" + }, + "ipv4": { + "config": { + "source-address": "13.1.1.1/32", + "destination-address": "23.1.1.1/32", + "dscp": 1, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 101, + "destination-port": 201 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 2, + "config": { + "sequence-id": 2, + "description": "Description for MyACL3 Rule Seq 2" + }, + "ipv4": { + "config": { + "source-address": "13.1.1.2/32", + "destination-address": "23.1.1.2/32", + "dscp": 2, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 102, + "destination-port": 202 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 3, + "config": { + "sequence-id": 3, + "description": "Description for MyACL3 Rule Seq 3" + }, + "ipv4": { + "config": { + "source-address": "13.1.1.3/32", + "destination-address": "23.1.1.3/32", + "dscp": 3, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 103, + "destination-port": 203 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 4, + "config": { + "sequence-id": 4, + "description": "Description for MyACL3 Rule Seq 4" + }, + "ipv4": { + "config": { + "source-address": "13.1.1.4/32", + "destination-address": "23.1.1.4/32", + "dscp": 4, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 104, + "destination-port": 204 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 5, + "config": { + "sequence-id": 5, + "description": "Description for MyACL3 Rule Seq 5" + }, + "ipv4": { + "config": { + "source-address": "13.1.1.5/32", + "destination-address": "23.1.1.5/32", + "dscp": 5, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 105, + "destination-port": 205 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + } + ] + } + } + ] +} diff --git a/src/translib/test/acl/03_create_MyACL1_5_more_rules.json b/src/translib/test/acl/03_create_MyACL1_5_more_rules.json new file mode 100644 index 0000000000..47d7d90413 --- /dev/null +++ b/src/translib/test/acl/03_create_MyACL1_5_more_rules.json @@ -0,0 +1,134 @@ +{ + "acl-entry": [ + { + "sequence-id": 6, + "config": { + "sequence-id": 6, + "description": "Description for MyACL1 Rule Seq 6" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.6/32", + "destination-address": "21.1.1.6/32", + "dscp": 6, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 106, + "destination-port": 206 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 7, + "config": { + "sequence-id": 7, + "description": "Description for MyACL1 Rule Seq 7" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.7/32", + "destination-address": "21.1.1.7/32", + "dscp": 7, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 107, + "destination-port": 207 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 8, + "config": { + "sequence-id": 8, + "description": "Description for MyACL1 Rule Seq 8" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.8/32", + "destination-address": "21.1.1.8/32", + "dscp": 8, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 108, + "destination-port": 208 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 9, + "config": { + "sequence-id": 9, + "description": "Description for MyACL1 Rule Seq 9" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.9/32", + "destination-address": "21.1.1.9/32", + "dscp": 9, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 109, + "destination-port": 209 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 10, + "config": { + "sequence-id": 10, + "description": "Description for MyACL1 Rule Seq 10" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.10/32", + "destination-address": "21.1.1.10/32", + "dscp": 10, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 110, + "destination-port": 210 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + } + ] +} diff --git a/src/translib/test/acl/04_create_MyACL1_1Rule_content.json b/src/translib/test/acl/04_create_MyACL1_1Rule_content.json new file mode 100644 index 0000000000..fe9dfeb5b3 --- /dev/null +++ b/src/translib/test/acl/04_create_MyACL1_1Rule_content.json @@ -0,0 +1,25 @@ +{ + "config": { + "sequence-id": 11, + "description": "Description for MyACL1 Rule Seq 11" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.11/32", + "destination-address": "21.1.1.11/32", + "dscp": 11, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 111, + "destination-port": 211 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } +} diff --git a/src/translib/test/acl/05_Create_MyACL3_binding.json b/src/translib/test/acl/05_Create_MyACL3_binding.json new file mode 100644 index 0000000000..4847c06cf6 --- /dev/null +++ b/src/translib/test/acl/05_Create_MyACL3_binding.json @@ -0,0 +1,28 @@ +{ + "interface": [ + { + "id": "Ethernet8", + "config": { + "id": "Ethernet8" + }, + "interface-ref": { + "config": { + "interface": "Ethernet8" + } + }, + "ingress-acl-sets": { + "ingress-acl-set": [ + { + "set-name": "MyACL3", + "type": "ACL_IPV4", + "config": { + "set-name": "MyACL3", + "type": "ACL_IPV4" + } + } + ] + } + } + ] +} + diff --git a/src/translib/test/acl/08_update_AclSets_MyACL3.json b/src/translib/test/acl/08_update_AclSets_MyACL3.json new file mode 100644 index 0000000000..d50aea7b97 --- /dev/null +++ b/src/translib/test/acl/08_update_AclSets_MyACL3.json @@ -0,0 +1,149 @@ +{ + "acl-sets": { + "acl-set": [ + { + "name": "MyACL3", + "type": "ACL_IPV4", + "config": { + "name": "MyACL3", + "type": "ACL_IPV4", + "description": "Description for MyACL3" + }, + "acl-entries": { + "acl-entry": [ + { + "sequence-id": 1, + "config": { + "sequence-id": 1, + "description": "Description for MyACL3 Rule Seq 1" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.1/32", + "destination-address": "21.1.1.1/32", + "dscp": 1, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 101, + "destination-port": 201 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 2, + "config": { + "sequence-id": 2, + "description": "Description for MyACL3 Rule Seq 2" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.2/32", + "destination-address": "21.1.1.2/32", + "dscp": 2, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 102, + "destination-port": 202 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 3, + "config": { + "sequence-id": 3, + "description": "Description for MyACL3 Rule Seq 3" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.3/32", + "destination-address": "21.1.1.3/32", + "dscp": 3, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 103, + "destination-port": 203 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 4, + "config": { + "sequence-id": 4, + "description": "Description for MyACL3 Rule Seq 4" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.4/32", + "destination-address": "21.1.1.4/32", + "dscp": 4, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 104, + "destination-port": 204 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 5, + "config": { + "sequence-id": 5, + "description": "Description for MyACL3 Rule Seq 5" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.5/32", + "destination-address": "21.1.1.5/32", + "dscp": 5, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 105, + "destination-port": 205 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + } + ] + } + } + ] + } +} diff --git a/src/translib/test/acl/09_after_delete_1Rule_get_MyACL4_command.txt b/src/translib/test/acl/09_after_delete_1Rule_get_MyACL4_command.txt new file mode 100644 index 0000000000..d6b71a0cd9 --- /dev/null +++ b/src/translib/test/acl/09_after_delete_1Rule_get_MyACL4_command.txt @@ -0,0 +1,2 @@ +./gnmi_get -xpath /openconfig-acl:acl/acl-sets/acl-set[name=MyACL4][type=ACL_IPV4] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty + diff --git a/src/translib/test/acl/09_after_delete_1Rule_get_MyACL4_response.json b/src/translib/test/acl/09_after_delete_1Rule_get_MyACL4_response.json new file mode 100644 index 0000000000..60caecd588 --- /dev/null +++ b/src/translib/test/acl/09_after_delete_1Rule_get_MyACL4_response.json @@ -0,0 +1 @@ +{\"openconfig-acl:acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":3},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.3/32\"},\"state\":{\"destination-address\":\"22.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.3/32\"}},\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3},\"transport\":{\"config\":{\"destination-port\":203,\"source-port\":103}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":5},\"ipv4\":{\"config\":{\"destination-address\":\"22.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.5/32\"},\"state\":{\"destination-address\":\"22.1.1.5/32\",\"dscp\":5,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"12.1.1.5/32\"}},\"sequence-id\":5,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":5},\"transport\":{\"config\":{\"destination-port\":205,\"source-port\":105}}}]},\"config\":{\"description\":\"Description for MyACL4\",\"name\":\"MyACL4\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL4\",\"state\":{\"description\":\"Description for MyACL4\",\"name\":\"MyACL4\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]} diff --git a/src/translib/test/acl/09_create_MyACL4_4Rules.json b/src/translib/test/acl/09_create_MyACL4_4Rules.json new file mode 100644 index 0000000000..87bc0e5101 --- /dev/null +++ b/src/translib/test/acl/09_create_MyACL4_4Rules.json @@ -0,0 +1,123 @@ +{ + "acl-sets": { + "acl-set": [ + { + "name": "MyACL4", + "type": "ACL_IPV4", + "config": { + "name": "MyACL4", + "type": "ACL_IPV4", + "description": "Description for MyACL4" + }, + "acl-entries": { + "acl-entry": [ + { + "sequence-id": 1, + "config": { + "sequence-id": 1, + "description": "Description for MyACL4 Rule Seq 1" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.1/32", + "destination-address": "21.1.1.1/32", + "dscp": 1, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 101, + "destination-port": 201 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 2, + "config": { + "sequence-id": 2, + "description": "Description for MyACL4 Rule Seq 2" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.2/32", + "destination-address": "21.1.1.2/32", + "dscp": 2, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 102, + "destination-port": 202 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + }, + { + "sequence-id": 3, + "config": { + "sequence-id": 3, + "description": "Description for MyACL4 Rule for Seq 3" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.3/32", + "destination-address": "21.1.1.3/32", + "dscp": 3, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 103, + "destination-port": 203 + } + }, + "actions": { + "config": { + "forwarding-action": "ACCEPT" + } + } + }, + { + "sequence-id": 4, + "config": { + "sequence-id": 4, + "description": "Description for MyACL4 Rule Seq 4" + }, + "ipv4": { + "config": { + "source-address": "11.1.1.4/32", + "destination-address": "21.1.1.4/32", + "dscp": 4, + "protocol": "IP_TCP" + } + }, + "transport": { + "config": { + "source-port": 104, + "destination-port": 204 + } + }, + "actions": { + "config": { + "forwarding-action": "DROP" + } + } + } + ] + } + } + ] + } +} diff --git a/src/translib/test/acl/09_create_MyACL4_4Rules_command.txt b/src/translib/test/acl/09_create_MyACL4_4Rules_command.txt new file mode 100644 index 0000000000..3185165a85 --- /dev/null +++ b/src/translib/test/acl/09_create_MyACL4_4Rules_command.txt @@ -0,0 +1 @@ +./gnmi_set -replace /openconfig-acl:acl/:@./09_create_MyACL4_4Rules.json -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/acl/09_delete_1Rule_MyACL4_command.txt b/src/translib/test/acl/09_delete_1Rule_MyACL4_command.txt new file mode 100644 index 0000000000..285ce3532c --- /dev/null +++ b/src/translib/test/acl/09_delete_1Rule_MyACL4_command.txt @@ -0,0 +1 @@ +./gnmi_set -delete /openconfig-acl:acl/acl-sets/acl-set[name=MyACL4][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=1] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/acl/09_get_1Rule_MyACL4_command.txt b/src/translib/test/acl/09_get_1Rule_MyACL4_command.txt new file mode 100644 index 0000000000..0c206dc7cf --- /dev/null +++ b/src/translib/test/acl/09_get_1Rule_MyACL4_command.txt @@ -0,0 +1,2 @@ +./gnmi_get -xpath /openconfig-acl:acl/acl-sets/acl-set[name=MyACL4][type=ACL_IPV4]/acl-entries/acl-entry[sequence-id=1] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty + diff --git a/src/translib/test/acl/09_get_all_MyACL4_4Rules_command.txt b/src/translib/test/acl/09_get_all_MyACL4_4Rules_command.txt new file mode 100644 index 0000000000..3616e967cb --- /dev/null +++ b/src/translib/test/acl/09_get_all_MyACL4_4Rules_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-acl:acl/acl-sets/acl-set[name=MyACL4][type=ACL_IPV4] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/acl/09_get_all_MyACL4_4Rules_response.json b/src/translib/test/acl/09_get_all_MyACL4_4Rules_response.json new file mode 100644 index 0000000000..9c149c65fe --- /dev/null +++ b/src/translib/test/acl/09_get_all_MyACL4_4Rules_response.json @@ -0,0 +1 @@ +{\"openconfig-acl:acl-set\":[{\"acl-entries\":{\"acl-entry\":[{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":1},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.1/32\"},\"state\":{\"destination-address\":\"21.1.1.1/32\",\"dscp\":1,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.1/32\"}},\"sequence-id\":1,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":1},\"transport\":{\"config\":{\"destination-port\":201,\"source-port\":101,\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\"]},\"state\":{\"tcp-flags\":[\"openconfig-packet-match-types:TCP_FIN\"]}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":2},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.2/32\",\"protocol\":\"openconfig-packet-match-types:IP_ICMP\",\"source-address\":\"11.1.1.2/32\"},\"state\":{\"destination-address\":\"21.1.1.2/32\",\"protocol\":\"openconfig-packet-match-types:IP_ICMP\",\"source-address\":\"11.1.1.2/32\"}},\"sequence-id\":2,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":2}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:ACCEPT\"}},\"config\":{\"sequence-id\":3},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.3/32\"},\"state\":{\"destination-address\":\"21.1.1.3/32\",\"dscp\":3,\"protocol\":\"openconfig-packet-match-types:IP_TCP\",\"source-address\":\"11.1.1.3/32\"}},\"sequence-id\":3,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":3},\"transport\":{\"config\":{\"destination-port\":203,\"source-port\":103}}},{\"actions\":{\"config\":{\"forwarding-action\":\"openconfig-acl:DROP\"}},\"config\":{\"sequence-id\":4},\"ipv4\":{\"config\":{\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_PIM\",\"source-address\":\"11.1.1.4/32\"},\"state\":{\"destination-address\":\"21.1.1.4/32\",\"dscp\":4,\"protocol\":\"openconfig-packet-match-types:IP_PIM\",\"source-address\":\"11.1.1.4/32\"}},\"sequence-id\":4,\"state\":{\"matched-octets\":\"0\",\"matched-packets\":\"0\",\"sequence-id\":4}}]},\"config\":{\"description\":\"Description for MyACL4\",\"name\":\"MyACL4\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"name\":\"MyACL4\",\"state\":{\"description\":\"Description for MyACL4\",\"name\":\"MyACL4\",\"type\":\"openconfig-acl:ACL_IPV4\"},\"type\":\"openconfig-acl:ACL_IPV4\"}]} diff --git a/src/translib/test/interfaces/01_get_all_command.txt b/src/translib/test/interfaces/01_get_all_command.txt new file mode 100644 index 0000000000..78f72b779d --- /dev/null +++ b/src/translib/test/interfaces/01_get_all_command.txt @@ -0,0 +1,2 @@ + + ./gnmi_get -xpath /openconfig-interfaces:interfaces -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/01_get_all_response.json b/src/translib/test/interfaces/01_get_all_response.json new file mode 100644 index 0000000000..4f3453c3c4 --- /dev/null +++ b/src/translib/test/interfaces/01_get_all_response.json @@ -0,0 +1 @@ +{\"openconfig-interfaces:interfaces\":{\"interface\":[{\"config\":{\"mtu\":9100,\"name\":\"Ethernet0\"},\"name\":\"Ethernet0\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":1,\"mtu\":9100,\"name\":\"Ethernet0\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.0\",\"prefix-length\":31},\"ip\":\"10.0.0.0\",\"state\":{\"ip\":\"10.0.0.0\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet100\"},\"name\":\"Ethernet100\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":26,\"mtu\":9100,\"name\":\"Ethernet100\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.50\",\"prefix-length\":31},\"ip\":\"10.0.0.50\",\"state\":{\"ip\":\"10.0.0.50\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet104\"},\"name\":\"Ethernet104\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":27,\"mtu\":9100,\"name\":\"Ethernet104\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.52\",\"prefix-length\":31},\"ip\":\"10.0.0.52\",\"state\":{\"ip\":\"10.0.0.52\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet108\"},\"name\":\"Ethernet108\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":28,\"mtu\":9100,\"name\":\"Ethernet108\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.54\",\"prefix-length\":31},\"ip\":\"10.0.0.54\",\"state\":{\"ip\":\"10.0.0.54\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet112\"},\"name\":\"Ethernet112\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":29,\"mtu\":9100,\"name\":\"Ethernet112\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.56\",\"prefix-length\":31},\"ip\":\"10.0.0.56\",\"state\":{\"ip\":\"10.0.0.56\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet116\"},\"name\":\"Ethernet116\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":30,\"mtu\":9100,\"name\":\"Ethernet116\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.58\",\"prefix-length\":31},\"ip\":\"10.0.0.58\",\"state\":{\"ip\":\"10.0.0.58\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet12\"},\"name\":\"Ethernet12\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":4,\"mtu\":9100,\"name\":\"Ethernet12\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.6\",\"prefix-length\":31},\"ip\":\"10.0.0.6\",\"state\":{\"ip\":\"10.0.0.6\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet120\"},\"name\":\"Ethernet120\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":31,\"mtu\":9100,\"name\":\"Ethernet120\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.60\",\"prefix-length\":31},\"ip\":\"10.0.0.60\",\"state\":{\"ip\":\"10.0.0.60\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet124\"},\"name\":\"Ethernet124\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":32,\"mtu\":9100,\"name\":\"Ethernet124\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.62\",\"prefix-length\":31},\"ip\":\"10.0.0.62\",\"state\":{\"ip\":\"10.0.0.62\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet16\"},\"name\":\"Ethernet16\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":5,\"mtu\":9100,\"name\":\"Ethernet16\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.8\",\"prefix-length\":31},\"ip\":\"10.0.0.8\",\"state\":{\"ip\":\"10.0.0.8\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet20\"},\"name\":\"Ethernet20\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":6,\"mtu\":9100,\"name\":\"Ethernet20\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.10\",\"prefix-length\":31},\"ip\":\"10.0.0.10\",\"state\":{\"ip\":\"10.0.0.10\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet24\"},\"name\":\"Ethernet24\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":7,\"mtu\":9100,\"name\":\"Ethernet24\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.12\",\"prefix-length\":31},\"ip\":\"10.0.0.12\",\"state\":{\"ip\":\"10.0.0.12\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet28\"},\"name\":\"Ethernet28\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":8,\"mtu\":9100,\"name\":\"Ethernet28\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.14\",\"prefix-length\":31},\"ip\":\"10.0.0.14\",\"state\":{\"ip\":\"10.0.0.14\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet32\"},\"name\":\"Ethernet32\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":9,\"mtu\":9100,\"name\":\"Ethernet32\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.16\",\"prefix-length\":31},\"ip\":\"10.0.0.16\",\"state\":{\"ip\":\"10.0.0.16\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet36\"},\"name\":\"Ethernet36\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":10,\"mtu\":9100,\"name\":\"Ethernet36\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.18\",\"prefix-length\":31},\"ip\":\"10.0.0.18\",\"state\":{\"ip\":\"10.0.0.18\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":1500,\"name\":\"Ethernet4\"},\"name\":\"Ethernet4\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":2,\"mtu\":1500,\"name\":\"Ethernet4\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.2\",\"prefix-length\":31},\"ip\":\"10.0.0.2\",\"state\":{\"ip\":\"10.0.0.2\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet40\"},\"name\":\"Ethernet40\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":11,\"mtu\":9100,\"name\":\"Ethernet40\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.20\",\"prefix-length\":31},\"ip\":\"10.0.0.20\",\"state\":{\"ip\":\"10.0.0.20\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet44\"},\"name\":\"Ethernet44\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":12,\"mtu\":9100,\"name\":\"Ethernet44\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.22\",\"prefix-length\":31},\"ip\":\"10.0.0.22\",\"state\":{\"ip\":\"10.0.0.22\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet48\"},\"name\":\"Ethernet48\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":13,\"mtu\":9100,\"name\":\"Ethernet48\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.24\",\"prefix-length\":31},\"ip\":\"10.0.0.24\",\"state\":{\"ip\":\"10.0.0.24\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet52\"},\"name\":\"Ethernet52\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":14,\"mtu\":9100,\"name\":\"Ethernet52\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.26\",\"prefix-length\":31},\"ip\":\"10.0.0.26\",\"state\":{\"ip\":\"10.0.0.26\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet56\"},\"name\":\"Ethernet56\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":15,\"mtu\":9100,\"name\":\"Ethernet56\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.28\",\"prefix-length\":31},\"ip\":\"10.0.0.28\",\"state\":{\"ip\":\"10.0.0.28\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet60\"},\"name\":\"Ethernet60\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":16,\"mtu\":9100,\"name\":\"Ethernet60\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.30\",\"prefix-length\":31},\"ip\":\"10.0.0.30\",\"state\":{\"ip\":\"10.0.0.30\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet64\"},\"name\":\"Ethernet64\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":17,\"mtu\":9100,\"name\":\"Ethernet64\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.32\",\"prefix-length\":31},\"ip\":\"10.0.0.32\",\"state\":{\"ip\":\"10.0.0.32\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet68\"},\"name\":\"Ethernet68\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":18,\"mtu\":9100,\"name\":\"Ethernet68\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.34\",\"prefix-length\":31},\"ip\":\"10.0.0.34\",\"state\":{\"ip\":\"10.0.0.34\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet72\"},\"name\":\"Ethernet72\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":19,\"mtu\":9100,\"name\":\"Ethernet72\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.36\",\"prefix-length\":31},\"ip\":\"10.0.0.36\",\"state\":{\"ip\":\"10.0.0.36\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet76\"},\"name\":\"Ethernet76\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":20,\"mtu\":9100,\"name\":\"Ethernet76\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.38\",\"prefix-length\":31},\"ip\":\"10.0.0.38\",\"state\":{\"ip\":\"10.0.0.38\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet8\"},\"name\":\"Ethernet8\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":3,\"mtu\":9100,\"name\":\"Ethernet8\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.4\",\"prefix-length\":31},\"ip\":\"10.0.0.4\",\"state\":{\"ip\":\"10.0.0.4\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet80\"},\"name\":\"Ethernet80\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":21,\"mtu\":9100,\"name\":\"Ethernet80\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.40\",\"prefix-length\":31},\"ip\":\"10.0.0.40\",\"state\":{\"ip\":\"10.0.0.40\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet84\"},\"name\":\"Ethernet84\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":22,\"mtu\":9100,\"name\":\"Ethernet84\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.42\",\"prefix-length\":31},\"ip\":\"10.0.0.42\",\"state\":{\"ip\":\"10.0.0.42\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet88\"},\"name\":\"Ethernet88\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":23,\"mtu\":9100,\"name\":\"Ethernet88\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.44\",\"prefix-length\":31},\"ip\":\"10.0.0.44\",\"state\":{\"ip\":\"10.0.0.44\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet92\"},\"name\":\"Ethernet92\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":24,\"mtu\":9100,\"name\":\"Ethernet92\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.46\",\"prefix-length\":31},\"ip\":\"10.0.0.46\",\"state\":{\"ip\":\"10.0.0.46\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"mtu\":9100,\"name\":\"Ethernet96\"},\"name\":\"Ethernet96\",\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":25,\"mtu\":9100,\"name\":\"Ethernet96\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.48\",\"prefix-length\":31},\"ip\":\"10.0.0.48\",\"state\":{\"ip\":\"10.0.0.48\",\"prefix-length\":31}}]}}}]}},{\"config\":{\"name\":\"PortConfigDone\"},\"name\":\"PortConfigDone\",\"state\":{\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"name\":\"PortConfigDone\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0}]}},{\"config\":{\"name\":\"PortInitDone\"},\"name\":\"PortInitDone\",\"state\":{\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"name\":\"PortInitDone\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0}]}}]}} diff --git a/src/translib/test/interfaces/02_get_interface_command.txt b/src/translib/test/interfaces/02_get_interface_command.txt new file mode 100644 index 0000000000..6ed4296924 --- /dev/null +++ b/src/translib/test/interfaces/02_get_interface_command.txt @@ -0,0 +1,2 @@ + + ./gnmi_get -xpath /openconfig-interfaces:interfaces/interface[name=Ethernet0] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/02_get_interface_response.json b/src/translib/test/interfaces/02_get_interface_response.json new file mode 100644 index 0000000000..378722e538 --- /dev/null +++ b/src/translib/test/interfaces/02_get_interface_response.json @@ -0,0 +1 @@ +{\"openconfig-interfaces:interface\":[{\"config\":{\"description\":\"\",\"mtu\":9100,\"name\":\"Ethernet0\"},\"name\":\"Ethernet0\",\"openconfig-if-ethernet:ethernet\":{\"config\":{\"port-speed\":\"openconfig-if-ethernet:SPEED_40GB\"},\"state\":{\"port-speed\":\"openconfig-if-ethernet:SPEED_40GB\"}},\"state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"description\":\"\",\"ifindex\":0,\"mtu\":9100,\"name\":\"Ethernet0\",\"oper-status\":\"DOWN\"},\"subinterfaces\":{\"subinterface\":[{\"index\":0,\"openconfig-if-ip:ipv4\":{\"addresses\":{\"address\":[{\"config\":{\"ip\":\"10.0.0.0\",\"prefix-length\":31},\"ip\":\"10.0.0.0\",\"state\":{\"ip\":\"10.0.0.0\",\"prefix-length\":31}}]}}}]}}]} diff --git a/src/translib/test/interfaces/03_get_interface_config_command.txt b/src/translib/test/interfaces/03_get_interface_config_command.txt new file mode 100644 index 0000000000..ecc26ae85f --- /dev/null +++ b/src/translib/test/interfaces/03_get_interface_config_command.txt @@ -0,0 +1,2 @@ + + ./gnmi_get -xpath /openconfig-interfaces:interfaces/interface[name=Ethernet0]/config/ -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/03_get_interface_config_response.json b/src/translib/test/interfaces/03_get_interface_config_response.json new file mode 100644 index 0000000000..8145d9011c --- /dev/null +++ b/src/translib/test/interfaces/03_get_interface_config_response.json @@ -0,0 +1 @@ +{\"openconfig-interfaces:config\":{\"mtu\":9100,\"name\":\"Ethernet0\"}} diff --git a/src/translib/test/interfaces/04_get_interface_state_command.txt b/src/translib/test/interfaces/04_get_interface_state_command.txt new file mode 100644 index 0000000000..699d12f50b --- /dev/null +++ b/src/translib/test/interfaces/04_get_interface_state_command.txt @@ -0,0 +1,2 @@ + +./gnmi_get -xpath /openconfig-interfaces:interfaces/interface[name=Ethernet0]/state -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/04_get_interface_state_response.json b/src/translib/test/interfaces/04_get_interface_state_response.json new file mode 100644 index 0000000000..f5909da373 --- /dev/null +++ b/src/translib/test/interfaces/04_get_interface_state_response.json @@ -0,0 +1 @@ +{\"openconfig-interfaces:state\":{\"admin-status\":\"UP\",\"counters\":{\"in-broadcast-pkts\":\"0\",\"in-discards\":\"0\",\"in-errors\":\"0\",\"in-multicast-pkts\":\"0\",\"in-octets\":\"0\",\"in-pkts\":\"0\",\"in-unicast-pkts\":\"0\",\"out-broadcast-pkts\":\"0\",\"out-discards\":\"0\",\"out-errors\":\"0\",\"out-multicast-pkts\":\"0\",\"out-octets\":\"0\",\"out-pkts\":\"0\",\"out-unicast-pkts\":\"0\"},\"ifindex\":1,\"mtu\":9100,\"name\":\"Ethernet0\",\"oper-status\":\"DOWN\"}} diff --git a/src/translib/test/interfaces/05_set_interface_mtu.json b/src/translib/test/interfaces/05_set_interface_mtu.json new file mode 100644 index 0000000000..f404cf3cfc --- /dev/null +++ b/src/translib/test/interfaces/05_set_interface_mtu.json @@ -0,0 +1 @@ +{"mtu":9000} diff --git a/src/translib/test/interfaces/05_set_interface_mtu_command.txt b/src/translib/test/interfaces/05_set_interface_mtu_command.txt new file mode 100644 index 0000000000..875e74c08c --- /dev/null +++ b/src/translib/test/interfaces/05_set_interface_mtu_command.txt @@ -0,0 +1 @@ +./gnmi_set -replace /openconfig-interfaces:interfaces/interface[name=Ethernet0]/config/mtu:@./05_set_interface_mtu.json -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/06_set_interface_ipv4.json b/src/translib/test/interfaces/06_set_interface_ipv4.json new file mode 100644 index 0000000000..863146b99b --- /dev/null +++ b/src/translib/test/interfaces/06_set_interface_ipv4.json @@ -0,0 +1 @@ +{"openconfig-if-ip:ipv4": {"addresses": {"address": [{"ip": "9.9.9.9","config": {"ip": "9.9.9.9","prefix-length": 24}}]}}} diff --git a/src/translib/test/interfaces/06_set_interface_ipv4_command.txt b/src/translib/test/interfaces/06_set_interface_ipv4_command.txt new file mode 100644 index 0000000000..c0258b5192 --- /dev/null +++ b/src/translib/test/interfaces/06_set_interface_ipv4_command.txt @@ -0,0 +1 @@ + ./gnmi_set -replace /openconfig-interfaces:interfaces/interface[name=Ethernet0]/subinterfaces/subinterface[index=0]:@./06_set_interface_ipv4.json -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/07_set_interface_ipv6.json b/src/translib/test/interfaces/07_set_interface_ipv6.json new file mode 100644 index 0000000000..f730c1b3f6 --- /dev/null +++ b/src/translib/test/interfaces/07_set_interface_ipv6.json @@ -0,0 +1 @@ +{"openconfig-if-ip:ipv6": {"addresses": {"address": [{"ip": "2001:db8::","config": {"ip": "2001:db8::","prefix-length": 32}}]}}} diff --git a/src/translib/test/interfaces/07_set_interface_ipv6_command.txt b/src/translib/test/interfaces/07_set_interface_ipv6_command.txt new file mode 100644 index 0000000000..e1ad7f0863 --- /dev/null +++ b/src/translib/test/interfaces/07_set_interface_ipv6_command.txt @@ -0,0 +1 @@ + ./gnmi_set -replace /openconfig-interfaces:interfaces/interface[name=Ethernet0]/subinterfaces/subinterface[index=0]:@./07_set_interface_ipv6.json -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/08_delete_interface_ipv4_command.txt b/src/translib/test/interfaces/08_delete_interface_ipv4_command.txt new file mode 100644 index 0000000000..f7fefcbbd6 --- /dev/null +++ b/src/translib/test/interfaces/08_delete_interface_ipv4_command.txt @@ -0,0 +1 @@ +./gnmi_set -delete /openconfig-interfaces:interfaces/interface[name=Ethernet0]/subinterfaces/subinterface[index=0]/ipv4/addresses/address[ip=9.9.9.9]/config/prefix-length -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/interfaces/09_delete_interface_ipv6_command.txt b/src/translib/test/interfaces/09_delete_interface_ipv6_command.txt new file mode 100644 index 0000000000..b6fd18f3cf --- /dev/null +++ b/src/translib/test/interfaces/09_delete_interface_ipv6_command.txt @@ -0,0 +1 @@ +./gnmi_set -delete /openconfig-interfaces:interfaces/interface[name=Ethernet0]/subinterfaces/subinterface[index=0]/ipv6/addresses/address[ip=2001:db8::]/config/prefix-length -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/lldp/01-get-all-intf-command.txt b/src/translib/test/lldp/01-get-all-intf-command.txt new file mode 100644 index 0000000000..92ead76a6b --- /dev/null +++ b/src/translib/test/lldp/01-get-all-intf-command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-lldp:lldp/interfaces -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/lldp/01-get-all-intf-response.json b/src/translib/test/lldp/01-get-all-intf-response.json new file mode 100644 index 0000000000..a4a6531dd6 --- /dev/null +++ b/src/translib/test/lldp/01-get-all-intf-response.json @@ -0,0 +1 @@ +{\"openconfig-lldp:interfaces\":{\"interface\":[{\"name\":\"Ethernet0\",\"neighbors\":{\"neighbor\":[{\"capabilities\":{\"capability\":[{\"name\":\"openconfig-lldp-types:MAC_BRIDGE\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:MAC_BRIDGE\"}},{\"name\":\"openconfig-lldp-types:REPEATER\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:REPEATER\"}},{\"name\":\"openconfig-lldp-types:ROUTER\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:ROUTER\"}}]},\"id\":\"Ethernet0\",\"state\":{\"chassis-id\":\"e4:f0:04:fe:64:e1\",\"chassis-id-type\":\"MAC_ADDRESS\",\"id\":\"1\",\"management-address\":\"\",\"port-description\":\"ethernet1/1/9\",\"port-id\":\"ethernet1/1/9\",\"port-id-type\":\"INTERFACE_NAME\",\"system-description\":\"Dell EMC Networking OS10-Enterprise.\\r\\nCopyright (c) 1999-2019 by Dell Inc. All Rights Reserved.\\r\\nSystem Description: OS10 Enterprise.\\r\\nOS Version: 10.4.3.1.\\r\\nSystem Type: S4148T-ON\",\"system-name\":\"swtor-b2lab2-1409\",\"ttl\":11666}}]}},{\"name\":\"eth0\",\"neighbors\":{\"neighbor\":[{\"capabilities\":{\"capability\":[{\"name\":\"openconfig-lldp-types:MAC_BRIDGE\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:MAC_BRIDGE\"}},{\"name\":\"openconfig-lldp-types:REPEATER\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:REPEATER\"}},{\"name\":\"openconfig-lldp-types:ROUTER\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:ROUTER\"}}]},\"id\":\"eth0\",\"state\":{\"chassis-id\":\"e4:f0:04:fe:64:e1\",\"chassis-id-type\":\"MAC_ADDRESS\",\"id\":\"1\",\"management-address\":\"\",\"port-description\":\"ethernet1/1/9\",\"port-id\":\"ethernet1/1/9\",\"port-id-type\":\"INTERFACE_NAME\",\"system-description\":\"Dell EMC Networking OS10-Enterprise.\\r\\nCopyright (c) 1999-2019 by Dell Inc. All Rights Reserved.\\r\\nSystem Description: OS10 Enterprise.\\r\\nOS Version: 10.4.3.1.\\r\\nSystem Type: S4148T-ON\",\"system-name\":\"swtor-b2lab2-1409\",\"ttl\":11666}}]}}]}} diff --git a/src/translib/test/lldp/02-get-one-intf-command.txt b/src/translib/test/lldp/02-get-one-intf-command.txt new file mode 100644 index 0000000000..e023e77254 --- /dev/null +++ b/src/translib/test/lldp/02-get-one-intf-command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-lldp:lldp/interfaces/interface[name=Ethernet0] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/lldp/02-get-one-intf-response.json b/src/translib/test/lldp/02-get-one-intf-response.json new file mode 100644 index 0000000000..a8a22cbbd2 --- /dev/null +++ b/src/translib/test/lldp/02-get-one-intf-response.json @@ -0,0 +1 @@ +{\"openconfig-lldp:interface\":[{\"name\":\"Ethernet0\",\"neighbors\":{\"neighbor\":[{\"capabilities\":{\"capability\":[{\"name\":\"openconfig-lldp-types:MAC_BRIDGE\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:MAC_BRIDGE\"}},{\"name\":\"openconfig-lldp-types:REPEATER\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:REPEATER\"}},{\"name\":\"openconfig-lldp-types:ROUTER\",\"state\":{\"enabled\":true,\"name\":\"openconfig-lldp-types:ROUTER\"}}]},\"id\":\"Ethernet0\",\"state\":{\"chassis-id\":\"e4:f0:04:fe:64:e1\",\"chassis-id-type\":\"MAC_ADDRESS\",\"id\":\"1\",\"management-address\":\"\",\"port-description\":\"ethernet1/1/9\",\"port-id\":\"ethernet1/1/9\",\"port-id-type\":\"INTERFACE_NAME\",\"system-description\":\"Dell EMC Networking OS10-Enterprise.\\r\\nCopyright (c) 1999-2019 by Dell Inc. All Rights Reserved.\\r\\nSystem Description: OS10 Enterprise.\\r\\nOS Version: 10.4.3.1.\\r\\nSystem Type: S4148T-ON\",\"system-name\":\"swtor-b2lab2-1409\",\"ttl\":1371}}]}}]} diff --git a/src/translib/test/platform/01_get_all_components.txt b/src/translib/test/platform/01_get_all_components.txt new file mode 100644 index 0000000000..a71bcadf15 --- /dev/null +++ b/src/translib/test/platform/01_get_all_components.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-platform:components -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/platform/01_get_all_response.json b/src/translib/test/platform/01_get_all_response.json new file mode 100644 index 0000000000..4c3ce02766 --- /dev/null +++ b/src/translib/test/platform/01_get_all_response.json @@ -0,0 +1 @@ +{\"openconfig-platform:components\":{\"component\":[{\"name\":\"System Eeprom\",\"state\":{\"empty\":false,\"hardware-version\":\"A00\",\"id\":\"S6000-ON\",\"location\":\"Slot 1\",\"mfg-name\":\"Dell\",\"name\" :\"System Eeprom\",\"oper-status\":\"openconfig-platform-types:ACTIVE\",\"part-no\":\"08YWFG\",\"removable\":false,\"serial-no\":\"CN08YWFG282983AR0146A00\"}}]}} diff --git a/src/translib/test/platform/02_get_one_component.txt b/src/translib/test/platform/02_get_one_component.txt new file mode 100644 index 0000000000..d863fe923b --- /dev/null +++ b/src/translib/test/platform/02_get_one_component.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-platform:components/component -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/platform/02_get_one_component_response.json b/src/translib/test/platform/02_get_one_component_response.json new file mode 100644 index 0000000000..c2d9a8aab4 --- /dev/null +++ b/src/translib/test/platform/02_get_one_component_response.json @@ -0,0 +1 @@ +{\"openconfig-platform:component\":[{\"name\":\"System Eeprom\",\"state\":{\"empty\":false,\"hardware-version\":\"A00\",\"id\":\"S6000-ON\",\"location\":\"Slot 1\",\"mfg-name\":\"Dell\",\"name\":\"System Eeprom\",\"oper-status\":\"openconfig-platform-types:ACTIVE\",\"part-no\":\"08YWFG\",\"removable\":false,\"serial-no\":\"CN08YWFG282983AR0146A00\"}}]} diff --git a/src/translib/test/platform/03_get_component_attribute_command.txt b/src/translib/test/platform/03_get_component_attribute_command.txt new file mode 100644 index 0000000000..c317db7947 --- /dev/null +++ b/src/translib/test/platform/03_get_component_attribute_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-platform:components/component[name="System Eeprom"] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/platform/03_get_component_attribute_response.json b/src/translib/test/platform/03_get_component_attribute_response.json new file mode 100644 index 0000000000..31454a6987 --- /dev/null +++ b/src/translib/test/platform/03_get_component_attribute_response.json @@ -0,0 +1 @@ +{\"openconfig-platform:name\":\"System Eeprom\",\"openconfig-platform:state\":{\"empty\":false,\"hardware-version\":\"A00\",\"id\":\"S6000-ON\",\"location\":\"Slot 1\",\"mfg-name\":\"Dell\",\"name\":\"System Eeprom\",\"oper-status\":\"openconfig-platform-types:ACTIVE\",\"part-no\":\"08YWFG\",\"removable\":false,\"serial-no\":\"CN08YWFG282983AR0146A00\"}} diff --git a/src/translib/test/system/01_get_all_components.txt b/src/translib/test/system/01_get_all_components.txt new file mode 100644 index 0000000000..ba02f03e35 --- /dev/null +++ b/src/translib/test/system/01_get_all_components.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-system:system -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/01_get_all_components_response.json b/src/translib/test/system/01_get_all_components_response.json new file mode 100644 index 0000000000..364e8c8066 --- /dev/null +++ b/src/translib/test/system/01_get_all_components_response.json @@ -0,0 +1 @@ +{\"openconfig-system:system\":{\"cpus\":{\"cpu\":[{\"index\":0,\"state\":{\"idle\":{\"instant\":62},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}},{\"index\":1,\"state\":{\"idle\":{\"instant\":62},\"kernel\":{\"instant\":17},\"user\":{\"instant\":16}}},{\"index\":2,\"state\":{\"idle\":{\"instant\":62},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}},{\"index\":3,\"state\":{\"idle\":{\"instant\":63},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}},{\"index\":4,\"state\":{\"idle\":{\"instant\":63},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}}]},\"processes\":{\"process\":[{\"pid\":\"1\",\"state\":{\"cpu-usage-system\":\"601\",\"cpu-usage-user\":\"542\",\"cpu-utilization\":0,\"memory-usage\":\"58884096\",\"memory-utilization\":0,\"name\":\"/sbin/init\",\"pid\":\"1\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"10\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[lru-add-drain]\",\"pid\":\"10\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"10103\",\"state\":{\"cpu-usage-system\":\"298\",\"cpu-usage-user\":\"742\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/cf3ef3c803b8b239b51280a696d30205ec7286b12e7ac7ef5ea5921e305957a0 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"10103\",\"start-time\":\"1565051498000000000\",\"uptime\":\"80298\"}},{\"pid\":\"10148\",\"state\":{\"cpu-usage-system\":\"1033\",\"cpu-usage-user\":\"18606\",\"cpu-utilization\":0,\"memory-usage\":\"60203008\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"10148\",\"start-time\":\"1565051498000000000\",\"uptime\":\"80298\"}},{\"pid\":\"10487\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"12124160\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/local/bin/swss.sh wait\",\"pid\":\"10487\",\"start-time\":\"1565051501000000000\",\"uptime\":\"80295\"}},{\"pid\":\"105\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"105\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"10545\",\"state\":{\"cpu-usage-system\":\"6\",\"cpu-usage-user\":\"32\",\"cpu-utilization\":0,\"memory-usage\":\"42135552\",\"memory-utilization\":0,\"name\":\"python /usr/bin/supervisor-proc-exit-listener\",\"pid\":\"10545\",\"start-time\":\"1565051501000000000\",\"uptime\":\"80295\"}},{\"pid\":\"107\",\"state\":{\"cpu-usage-system\":\"143\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:1H]\",\"pid\":\"107\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"108\",\"state\":{\"cpu-usage-system\":\"148\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:1H]\",\"pid\":\"108\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"10822\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"46514176\",\"memory-utilization\":0,\"name\":\"sudo -i\",\"pid\":\"10822\",\"start-time\":\"1565116326000000000\",\"uptime\":\"15470\"}},{\"pid\":\"10825\",\"state\":{\"cpu-usage-system\":\"23\",\"cpu-usage-user\":\"80\",\"cpu-utilization\":0,\"memory-usage\":\"24023040\",\"memory-utilization\":0,\"name\":\"-bash\",\"pid\":\"10825\",\"start-time\":\"1565116326000000000\",\"uptime\":\"15470\"}},{\"pid\":\"109\",\"state\":{\"cpu-usage-system\":\"123\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:1H]\",\"pid\":\"109\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"11\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"66\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/0]\",\"pid\":\"11\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"110\",\"state\":{\"cpu-usage-system\":\"162\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:1H]\",\"pid\":\"110\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"11204\",\"state\":{\"cpu-usage-system\":\"93\",\"cpu-usage-user\":\"50\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"11204\",\"start-time\":\"1565051513000000000\",\"uptime\":\"80283\"}},{\"pid\":\"11298\",\"state\":{\"cpu-usage-system\":\"254\",\"cpu-usage-user\":\"643\",\"cpu-utilization\":0,\"memory-usage\":\"109920256\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/82d6d3d26404f8b674a0a2492d77958e34e5c6e9c882672c0987d3e845afa9ea -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11298\",\"start-time\":\"1565051514000000000\",\"uptime\":\"80282\"}},{\"pid\":\"11353\",\"state\":{\"cpu-usage-system\":\"428\",\"cpu-usage-user\":\"5891\",\"cpu-utilization\":0,\"memory-usage\":\"59600896\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11353\",\"start-time\":\"1565051515000000000\",\"uptime\":\"80281\"}},{\"pid\":\"11370\",\"state\":{\"cpu-usage-system\":\"283\",\"cpu-usage-user\":\"623\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/43e3fcfd19714d24ce025c1fb295633e4e5ec8373aeaa6d874d20410b4c13d70 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11370\",\"start-time\":\"1565051515000000000\",\"uptime\":\"80281\"}},{\"pid\":\"11420\",\"state\":{\"cpu-usage-system\":\"282\",\"cpu-usage-user\":\"642\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/bf414527b1d6ae6537d3fe484bd98648bdd22af80d1c893f3a6981a0df91ae9a -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11420\",\"start-time\":\"1565051515000000000\",\"uptime\":\"80281\"}},{\"pid\":\"11430\",\"state\":{\"cpu-usage-system\":\"993\",\"cpu-usage-user\":\"946\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/176d1ac359df0611c615f0657280646398744e43ee251c75d950206f175e706d -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11430\",\"start-time\":\"1565051516000000000\",\"uptime\":\"80280\"}},{\"pid\":\"11488\",\"state\":{\"cpu-usage-system\":\"502\",\"cpu-usage-user\":\"4612\",\"cpu-utilization\":0,\"memory-usage\":\"59596800\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11488\",\"start-time\":\"1565051516000000000\",\"uptime\":\"80280\"}},{\"pid\":\"11498\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u8:0]\",\"pid\":\"11498\",\"start-time\":\"1565131504000000000\",\"uptime\":\"292\"}},{\"pid\":\"11533\",\"state\":{\"cpu-usage-system\":\"509\",\"cpu-usage-user\":\"6198\",\"cpu-utilization\":0,\"memory-usage\":\"61124608\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11533\",\"start-time\":\"1565051516000000000\",\"uptime\":\"80280\"}},{\"pid\":\"11543\",\"state\":{\"cpu-usage-system\":\"619\",\"cpu-usage-user\":\"4818\",\"cpu-utilization\":0,\"memory-usage\":\"58257408\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11543\",\"start-time\":\"1565051516000000000\",\"uptime\":\"80280\"}},{\"pid\":\"11588\",\"state\":{\"cpu-usage-system\":\"3032\",\"cpu-usage-user\":\"20830\",\"cpu-utilization\":0,\"memory-usage\":\"188989440\",\"memory-utilization\":0,\"name\":\"/usr/bin/orchagent -d /var/log/swss -b 8192 -m 90:b1:1c:f4:a8:e1\",\"pid\":\"11588\",\"start-time\":\"1565051517000000000\",\"uptime\":\"80279\"}},{\"pid\":\"11685\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/teamd.sh wait\",\"pid\":\"11685\",\"start-time\":\"1565051518000000000\",\"uptime\":\"80278\"}},{\"pid\":\"11687\",\"state\":{\"cpu-usage-system\":\"122\",\"cpu-usage-user\":\"1812\",\"cpu-utilization\":0,\"memory-usage\":\"409763840\",\"memory-utilization\":0,\"name\":\"docker wait teamd\",\"pid\":\"11687\",\"start-time\":\"1565051518000000000\",\"uptime\":\"80278\"}},{\"pid\":\"11779\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/radv.sh wait\",\"pid\":\"11779\",\"start-time\":\"1565051519000000000\",\"uptime\":\"80277\"}},{\"pid\":\"11783\",\"state\":{\"cpu-usage-system\":\"122\",\"cpu-usage-user\":\"1826\",\"cpu-utilization\":0,\"memory-usage\":\"410025984\",\"memory-utilization\":0,\"name\":\"docker wait radv\",\"pid\":\"11783\",\"start-time\":\"1565051519000000000\",\"uptime\":\"80277\"}},{\"pid\":\"11825\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12115968\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/mgmt-framework.sh wait\",\"pid\":\"11825\",\"start-time\":\"1565051520000000000\",\"uptime\":\"80276\"}},{\"pid\":\"11829\",\"state\":{\"cpu-usage-system\":\"114\",\"cpu-usage-user\":\"1813\",\"cpu-utilization\":0,\"memory-usage\":\"476872704\",\"memory-utilization\":0,\"name\":\"docker wait mgmt-framework\",\"pid\":\"11829\",\"start-time\":\"1565051520000000000\",\"uptime\":\"80276\"}},{\"pid\":\"12\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/0]\",\"pid\":\"12\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"12113\",\"state\":{\"cpu-usage-system\":\"50\",\"cpu-usage-user\":\"45\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"12113\",\"start-time\":\"1565051526000000000\",\"uptime\":\"80270\"}},{\"pid\":\"12159\",\"state\":{\"cpu-usage-system\":\"150\",\"cpu-usage-user\":\"87\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"12159\",\"start-time\":\"1565051526000000000\",\"uptime\":\"80270\"}},{\"pid\":\"12262\",\"state\":{\"cpu-usage-system\":\"56\",\"cpu-usage-user\":\"38\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"12262\",\"start-time\":\"1565051528000000000\",\"uptime\":\"80268\"}},{\"pid\":\"12370\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"20779008\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/snmp.sh wait\",\"pid\":\"12370\",\"start-time\":\"1565051529000000000\",\"uptime\":\"80267\"}},{\"pid\":\"12374\",\"state\":{\"cpu-usage-system\":\"1091\",\"cpu-usage-user\":\"956\",\"cpu-utilization\":0,\"memory-usage\":\"474906624\",\"memory-utilization\":0,\"name\":\"docker wait snmp\",\"pid\":\"12374\",\"start-time\":\"1565051529000000000\",\"uptime\":\"80267\"}},{\"pid\":\"12385\",\"state\":{\"cpu-usage-system\":\"250\",\"cpu-usage-user\":\"671\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/bc404b0673c5df675659fa32a2fceda2fefa2f8f51e42be622378f9af58d73c3 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"12385\",\"start-time\":\"1565051529000000000\",\"uptime\":\"80267\"}},{\"pid\":\"12459\",\"state\":{\"cpu-usage-system\":\"491\",\"cpu-usage-user\":\"3883\",\"cpu-utilization\":0,\"memory-usage\":\"59596800\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"12459\",\"start-time\":\"1565051530000000000\",\"uptime\":\"80266\"}},{\"pid\":\"12477\",\"state\":{\"cpu-usage-system\":\"1118\",\"cpu-usage-user\":\"53352\",\"cpu-utilization\":0,\"memory-usage\":\"1176498176\",\"memory-utilization\":1,\"name\":\"/usr/sbin/rest_server -ui /rest_ui -logtostderr -cert /tmp/cert.pem -key /tmp/key.pem\",\"pid\":\"12477\",\"start-time\":\"1565051530000000000\",\"uptime\":\"80266\"}},{\"pid\":\"12578\",\"state\":{\"cpu-usage-system\":\"277\",\"cpu-usage-user\":\"189\",\"cpu-utilization\":0,\"memory-usage\":\"102764544\",\"memory-utilization\":0,\"name\":\"/usr/bin/teammgrd\",\"pid\":\"12578\",\"start-time\":\"1565051531000000000\",\"uptime\":\"80265\"}},{\"pid\":\"12638\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/dhcp_relay.sh wait\",\"pid\":\"12638\",\"start-time\":\"1565051532000000000\",\"uptime\":\"80264\"}},{\"pid\":\"12640\",\"state\":{\"cpu-usage-system\":\"127\",\"cpu-usage-user\":\"1828\",\"cpu-utilization\":0,\"memory-usage\":\"476610560\",\"memory-utilization\":0,\"name\":\"docker wait dhcp_relay\",\"pid\":\"12640\",\"start-time\":\"1565051532000000000\",\"uptime\":\"80264\"}},{\"pid\":\"12643\",\"state\":{\"cpu-usage-system\":\"174907\",\"cpu-usage-user\":\"110295\",\"cpu-utilization\":0,\"memory-usage\":\"102756352\",\"memory-utilization\":0,\"name\":\"/usr/bin/portsyncd -p /usr/share/sonic/hwsku/port_config.ini\",\"pid\":\"12643\",\"start-time\":\"1565051532000000000\",\"uptime\":\"80264\"}},{\"pid\":\"12885\",\"state\":{\"cpu-usage-system\":\"263\",\"cpu-usage-user\":\"193\",\"cpu-utilization\":0,\"memory-usage\":\"111271936\",\"memory-utilization\":0,\"name\":\"/usr/bin/teamsyncd\",\"pid\":\"12885\",\"start-time\":\"1565051537000000000\",\"uptime\":\"80259\"}},{\"pid\":\"13\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/1]\",\"pid\":\"13\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"13092\",\"state\":{\"cpu-usage-system\":\"244\",\"cpu-usage-user\":\"674\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/7af68f8a31ef66afb620aa861b93a017988a73696ff7ae876df9da8181f57125 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"13092\",\"start-time\":\"1565051540000000000\",\"uptime\":\"80256\"}},{\"pid\":\"13108\",\"state\":{\"cpu-usage-system\":\"401\",\"cpu-usage-user\":\"697\",\"cpu-utilization\":0,\"memory-usage\":\"102719488\",\"memory-utilization\":0,\"name\":\"/usr/bin/neighsyncd\",\"pid\":\"13108\",\"start-time\":\"1565051540000000000\",\"uptime\":\"80256\"}},{\"pid\":\"13110\",\"state\":{\"cpu-usage-system\":\"478\",\"cpu-usage-user\":\"5523\",\"cpu-utilization\":0,\"memory-usage\":\"59600896\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"13110\",\"start-time\":\"1565051540000000000\",\"uptime\":\"80256\"}},{\"pid\":\"13171\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12152832\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/local/bin/syncd.sh wait\",\"pid\":\"13171\",\"start-time\":\"1565051541000000000\",\"uptime\":\"80255\"}},{\"pid\":\"13173\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"12140544\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/swss.sh wait\",\"pid\":\"13173\",\"start-time\":\"1565051541000000000\",\"uptime\":\"80255\"}},{\"pid\":\"13175\",\"state\":{\"cpu-usage-system\":\"118\",\"cpu-usage-user\":\"1824\",\"cpu-utilization\":0,\"memory-usage\":\"409763840\",\"memory-utilization\":0,\"name\":\"docker wait swss\",\"pid\":\"13175\",\"start-time\":\"1565051541000000000\",\"uptime\":\"80255\"}},{\"pid\":\"13177\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12115968\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/syncd.sh wait\",\"pid\":\"13177\",\"start-time\":\"1565051541000000000\",\"uptime\":\"80255\"}},{\"pid\":\"13179\",\"state\":{\"cpu-usage-system\":\"132\",\"cpu-usage-user\":\"1830\",\"cpu-utilization\":0,\"memory-usage\":\"477134848\",\"memory-utilization\":0,\"name\":\"docker wait syncd\",\"pid\":\"13179\",\"start-time\":\"1565051541000000000\",\"uptime\":\"80255\"}},{\"pid\":\"1323\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"15482880\",\"memory-utilization\":0,\"name\":\"/sbin/agetty --noclear tty1 linux\",\"pid\":\"1323\",\"start-time\":\"1565051404000000000\",\"uptime\":\"80392\"}},{\"pid\":\"13389\",\"state\":{\"cpu-usage-system\":\"603\",\"cpu-usage-user\":\"208\",\"cpu-utilization\":0,\"memory-usage\":\"100241408\",\"memory-utilization\":0,\"name\":\"/usr/sbin/ntpd -p /var/run/ntpd.pid -x -u 106:110\",\"pid\":\"13389\",\"start-time\":\"1565051544000000000\",\"uptime\":\"80252\"}},{\"pid\":\"13543\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u8:2]\",\"pid\":\"13543\",\"start-time\":\"1565130208000000000\",\"uptime\":\"1588\"}},{\"pid\":\"13656\",\"state\":{\"cpu-usage-system\":\"893\",\"cpu-usage-user\":\"38\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:2]\",\"pid\":\"13656\",\"start-time\":\"1565116353000000000\",\"uptime\":\"15443\"}},{\"pid\":\"13739\",\"state\":{\"cpu-usage-system\":\"70\",\"cpu-usage-user\":\"52\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"13739\",\"start-time\":\"1565051549000000000\",\"uptime\":\"80247\"}},{\"pid\":\"1381\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"14\",\"cpu-utilization\":0,\"memory-usage\":\"18616320\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"1381\",\"start-time\":\"1565054068000000000\",\"uptime\":\"77728\"}},{\"pid\":\"13971\",\"state\":{\"cpu-usage-system\":\"50\",\"cpu-usage-user\":\"43\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"13971\",\"start-time\":\"1565051552000000000\",\"uptime\":\"80244\"}},{\"pid\":\"14\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"69\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/1]\",\"pid\":\"14\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"14031\",\"state\":{\"cpu-usage-system\":\"258\",\"cpu-usage-user\":\"194\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"14031\",\"start-time\":\"1565051553000000000\",\"uptime\":\"80243\"}},{\"pid\":\"14035\",\"state\":{\"cpu-usage-system\":\"258\",\"cpu-usage-user\":\"192\",\"cpu-utilization\":0,\"memory-usage\":\"102875136\",\"memory-utilization\":0,\"name\":\"/usr/bin/vrfmgrd\",\"pid\":\"14035\",\"start-time\":\"1565051553000000000\",\"uptime\":\"80243\"}},{\"pid\":\"14046\",\"state\":{\"cpu-usage-system\":\"3\",\"cpu-usage-user\":\"4\",\"cpu-utilization\":0,\"memory-usage\":\"110243840\",\"memory-utilization\":0,\"name\":\"/usr/bin/dsserve /usr/bin/syncd --diag -u -p /etc/sai.d/sai.profile\",\"pid\":\"14046\",\"start-time\":\"1565051553000000000\",\"uptime\":\"80243\"}},{\"pid\":\"14301\",\"state\":{\"cpu-usage-system\":\"298443\",\"cpu-usage-user\":\"1851014\",\"cpu-utilization\":0,\"memory-usage\":\"1759907840\",\"memory-utilization\":3,\"name\":\"/usr/bin/syncd --diag -u -p /etc/sai.d/sai.profile\",\"pid\":\"14301\",\"start-time\":\"1565051557000000000\",\"uptime\":\"80239\"}},{\"pid\":\"14336\",\"state\":{\"cpu-usage-system\":\"1044\",\"cpu-usage-user\":\"760\",\"cpu-utilization\":0,\"memory-usage\":\"56475648\",\"memory-utilization\":0,\"name\":\"/usr/sbin/snmpd -f -LS4d -u Debian-snmp -g Debian-snmp -I -smux mteTrigger mteTriggerConf ifTable ifXTable inetCidrRouteTable ipCidrRouteTable ip disk_hw -p /run/snmpd.pid\",\"pid\":\"14336\",\"start-time\":\"1565051557000000000\",\"uptime\":\"80239\"}},{\"pid\":\"14399\",\"state\":{\"cpu-usage-system\":\"264\",\"cpu-usage-user\":\"192\",\"cpu-utilization\":0,\"memory-usage\":\"102780928\",\"memory-utilization\":0,\"name\":\"/usr/bin/vlanmgrd\",\"pid\":\"14399\",\"start-time\":\"1565051558000000000\",\"uptime\":\"80238\"}},{\"pid\":\"14545\",\"state\":{\"cpu-usage-system\":\"102872\",\"cpu-usage-user\":\"946118\",\"cpu-utilization\":0,\"memory-usage\":\"165548032\",\"memory-utilization\":0,\"name\":\"python3.6 -m sonic_ax_impl\",\"pid\":\"14545\",\"start-time\":\"1565051560000000000\",\"uptime\":\"80236\"}},{\"pid\":\"14666\",\"state\":{\"cpu-usage-system\":\"256\",\"cpu-usage-user\":\"215\",\"cpu-utilization\":0,\"memory-usage\":\"102862848\",\"memory-utilization\":0,\"name\":\"/usr/bin/intfmgrd\",\"pid\":\"14666\",\"start-time\":\"1565051561000000000\",\"uptime\":\"80235\"}},{\"pid\":\"14737\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"18374656\",\"memory-utilization\":0,\"name\":\"/bin/bash ./clish_start\",\"pid\":\"14737\",\"start-time\":\"1565111083000000000\",\"uptime\":\"20713\"}},{\"pid\":\"14739\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"28368896\",\"memory-utilization\":0,\"name\":\"/usr/sbin/cli/clish\",\"pid\":\"14739\",\"start-time\":\"1565111083000000000\",\"uptime\":\"20713\"}},{\"pid\":\"14930\",\"state\":{\"cpu-usage-system\":\"272\",\"cpu-usage-user\":\"190\",\"cpu-utilization\":0,\"memory-usage\":\"102731776\",\"memory-utilization\":0,\"name\":\"/usr/bin/portmgrd\",\"pid\":\"14930\",\"start-time\":\"1565051564000000000\",\"uptime\":\"80232\"}},{\"pid\":\"15\",\"state\":{\"cpu-usage-system\":\"7533\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/1]\",\"pid\":\"15\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"15235\",\"state\":{\"cpu-usage-system\":\"284\",\"cpu-usage-user\":\"3827\",\"cpu-utilization\":0,\"memory-usage\":\"102887424\",\"memory-utilization\":0,\"name\":\"/usr/bin/buffermgrd -l /usr/share/sonic/hwsku/pg_profile_lookup.ini\",\"pid\":\"15235\",\"start-time\":\"1565051567000000000\",\"uptime\":\"80229\"}},{\"pid\":\"15776\",\"state\":{\"cpu-usage-system\":\"269\",\"cpu-usage-user\":\"178\",\"cpu-utilization\":0,\"memory-usage\":\"102793216\",\"memory-utilization\":0,\"name\":\"/usr/bin/nbrmgrd\",\"pid\":\"15776\",\"start-time\":\"1565051573000000000\",\"uptime\":\"80223\"}},{\"pid\":\"16\",\"state\":{\"cpu-usage-system\":\"1045\",\"cpu-usage-user\":\"31\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/1]\",\"pid\":\"16\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"16057\",\"state\":{\"cpu-usage-system\":\"260\",\"cpu-usage-user\":\"195\",\"cpu-utilization\":0,\"memory-usage\":\"102846464\",\"memory-utilization\":0,\"name\":\"/usr/bin/vxlanmgrd\",\"pid\":\"16057\",\"start-time\":\"1565051577000000000\",\"uptime\":\"80219\"}},{\"pid\":\"16160\",\"state\":{\"cpu-usage-system\":\"3027\",\"cpu-usage-user\":\"1414\",\"cpu-utilization\":0,\"memory-usage\":\"143773696\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/xcvrd\",\"pid\":\"16160\",\"start-time\":\"1565051578000000000\",\"uptime\":\"80218\"}},{\"pid\":\"165\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u9:0]\",\"pid\":\"165\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"16944\",\"state\":{\"cpu-usage-system\":\"2953\",\"cpu-usage-user\":\"153\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:1]\",\"pid\":\"16944\",\"start-time\":\"1565074324000000000\",\"uptime\":\"57472\"}},{\"pid\":\"174\",\"state\":{\"cpu-usage-system\":\"720\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[jbd2/sda4-8]\",\"pid\":\"174\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"175\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ext4-rsv-conver]\",\"pid\":\"175\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"17545\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"6\",\"cpu-utilization\":0,\"memory-usage\":\"94949376\",\"memory-utilization\":0,\"name\":\"sshd: admin [priv]\",\"pid\":\"17545\",\"start-time\":\"1565130908000000000\",\"uptime\":\"888\"}},{\"pid\":\"179\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"179\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"17999\",\"state\":{\"cpu-usage-system\":\"42\",\"cpu-usage-user\":\"152\",\"cpu-utilization\":0,\"memory-usage\":\"24301568\",\"memory-utilization\":0,\"name\":\"-bash\",\"pid\":\"17999\",\"start-time\":\"1565113433000000000\",\"uptime\":\"18363\"}},{\"pid\":\"18\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:0H]\",\"pid\":\"18\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"181\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"181\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"18146\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"2\",\"cpu-utilization\":0,\"memory-usage\":\"94949376\",\"memory-utilization\":0,\"name\":\"sshd: admin@pts/0\",\"pid\":\"18146\",\"start-time\":\"1565130915000000000\",\"uptime\":\"881\"}},{\"pid\":\"18155\",\"state\":{\"cpu-usage-system\":\"6\",\"cpu-usage-user\":\"27\",\"cpu-utilization\":0,\"memory-usage\":\"24031232\",\"memory-utilization\":0,\"name\":\"-bash\",\"pid\":\"18155\",\"start-time\":\"1565130915000000000\",\"uptime\":\"881\"}},{\"pid\":\"182\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"182\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"184\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"184\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"186\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"186\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"188\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"188\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"189\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"189\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"19\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/2]\",\"pid\":\"19\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"190\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"190\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"196\",\"state\":{\"cpu-usage-system\":\"198\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[loop0]\",\"pid\":\"196\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"19894\",\"state\":{\"cpu-usage-system\":\"17\",\"cpu-usage-user\":\"43\",\"cpu-utilization\":0,\"memory-usage\":\"619483136\",\"memory-utilization\":0,\"name\":\"docker exec -it mgmt-framework bash\",\"pid\":\"19894\",\"start-time\":\"1565130933000000000\",\"uptime\":\"863\"}},{\"pid\":\"19993\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"12\",\"cpu-utilization\":0,\"memory-usage\":\"18620416\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"19993\",\"start-time\":\"1565130933000000000\",\"uptime\":\"863\"}},{\"pid\":\"2\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kthreadd]\",\"pid\":\"2\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"20\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"71\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/2]\",\"pid\":\"20\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"20098\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:1]\",\"pid\":\"20098\",\"start-time\":\"1565130605000000000\",\"uptime\":\"1191\"}},{\"pid\":\"20506\",\"state\":{\"cpu-usage-system\":\"938\",\"cpu-usage-user\":\"219\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:0]\",\"pid\":\"20506\",\"start-time\":\"1565052269000000000\",\"uptime\":\"79527\"}},{\"pid\":\"20820\",\"state\":{\"cpu-usage-system\":\"1583\",\"cpu-usage-user\":\"18\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:0]\",\"pid\":\"20820\",\"start-time\":\"1565117416000000000\",\"uptime\":\"14380\"}},{\"pid\":\"20903\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"69513216\",\"memory-utilization\":0,\"name\":\"/bin/login --\",\"pid\":\"20903\",\"start-time\":\"1565053934000000000\",\"uptime\":\"77862\"}},{\"pid\":\"21\",\"state\":{\"cpu-usage-system\":\"6888\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/2]\",\"pid\":\"21\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"21161\",\"state\":{\"cpu-usage-system\":\"5\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u8:1]\",\"pid\":\"21161\",\"start-time\":\"1565130946000000000\",\"uptime\":\"850\"}},{\"pid\":\"2138\",\"state\":{\"cpu-usage-system\":\"12\",\"cpu-usage-user\":\"72\",\"cpu-utilization\":0,\"memory-usage\":\"62746624\",\"memory-utilization\":0,\"name\":\"/usr/bin/python -u /usr/bin/hostcfgd\",\"pid\":\"2138\",\"start-time\":\"1565051412000000000\",\"uptime\":\"80384\"}},{\"pid\":\"2148\",\"state\":{\"cpu-usage-system\":\"10\",\"cpu-usage-user\":\"34\",\"cpu-utilization\":0,\"memory-usage\":\"57978880\",\"memory-utilization\":0,\"name\":\"python /usr/bin/caclmgrd\",\"pid\":\"2148\",\"start-time\":\"1565051412000000000\",\"uptime\":\"80384\"}},{\"pid\":\"21950\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"100\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:0]\",\"pid\":\"21950\",\"start-time\":\"1565130293000000000\",\"uptime\":\"1503\"}},{\"pid\":\"22\",\"state\":{\"cpu-usage-system\":\"1021\",\"cpu-usage-user\":\"5\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/2]\",\"pid\":\"22\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"220\",\"state\":{\"cpu-usage-system\":\"592\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[loop1]\",\"pid\":\"220\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"22164\",\"state\":{\"cpu-usage-system\":\"6\",\"cpu-usage-user\":\"19\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/c647cc0b7005f468d6c62da90019f93df6ad6376a5fc410f85edbe438da95b17 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"22164\",\"start-time\":\"1565130294000000000\",\"uptime\":\"1502\"}},{\"pid\":\"222\",\"state\":{\"cpu-usage-system\":\"198\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[jbd2/loop1-8]\",\"pid\":\"222\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"22206\",\"state\":{\"cpu-usage-system\":\"32\",\"cpu-usage-user\":\"262\",\"cpu-utilization\":0,\"memory-usage\":\"59600896\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"22206\",\"start-time\":\"1565130294000000000\",\"uptime\":\"1502\"}},{\"pid\":\"223\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ext4-rsv-conver]\",\"pid\":\"223\",\"start-time\":\"1565051369000000000\",\"uptime\":\"80427\"}},{\"pid\":\"22312\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12115968\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/telemetry.sh wait\",\"pid\":\"22312\",\"start-time\":\"1565130295000000000\",\"uptime\":\"1501\"}},{\"pid\":\"22316\",\"state\":{\"cpu-usage-system\":\"13\",\"cpu-usage-user\":\"46\",\"cpu-utilization\":0,\"memory-usage\":\"399929344\",\"memory-utilization\":0,\"name\":\"docker wait telemetry\",\"pid\":\"22316\",\"start-time\":\"1565130295000000000\",\"uptime\":\"1501\"}},{\"pid\":\"22318\",\"state\":{\"cpu-usage-system\":\"95\",\"cpu-usage-user\":\"6\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:0]\",\"pid\":\"22318\",\"start-time\":\"1565130295000000000\",\"uptime\":\"1501\"}},{\"pid\":\"22676\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"6\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"22676\",\"start-time\":\"1565130299000000000\",\"uptime\":\"1497\"}},{\"pid\":\"22849\",\"state\":{\"cpu-usage-system\":\"906\",\"cpu-usage-user\":\"2494\",\"cpu-utilization\":0,\"memory-usage\":\"760782848\",\"memory-utilization\":0,\"name\":\"telemetry -logtostderr --insecure --port 8080 --allow_no_client_auth -v=2\",\"pid\":\"22849\",\"start-time\":\"1565130301000000000\",\"uptime\":\"1495\"}},{\"pid\":\"23170\",\"state\":{\"cpu-usage-system\":\"938\",\"cpu-usage-user\":\"824\",\"cpu-utilization\":0,\"memory-usage\":\"607961088\",\"memory-utilization\":0,\"name\":\"/usr/sbin/dialout_client_cli -insecure -logtostderr -v 2\",\"pid\":\"23170\",\"start-time\":\"1565130305000000000\",\"uptime\":\"1491\"}},{\"pid\":\"23200\",\"state\":{\"cpu-usage-system\":\"320\",\"cpu-usage-user\":\"27\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:1]\",\"pid\":\"23200\",\"start-time\":\"1565125024000000000\",\"uptime\":\"6772\"}},{\"pid\":\"2362\",\"state\":{\"cpu-usage-system\":\"339\",\"cpu-usage-user\":\"320\",\"cpu-utilization\":0,\"memory-usage\":\"329822208\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"2362\",\"start-time\":\"1565051416000000000\",\"uptime\":\"80380\"}},{\"pid\":\"24\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:0H]\",\"pid\":\"24\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"25\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/3]\",\"pid\":\"25\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"251\",\"state\":{\"cpu-usage-system\":\"422\",\"cpu-usage-user\":\"251\",\"cpu-utilization\":0,\"memory-usage\":\"45461504\",\"memory-utilization\":0,\"name\":\"/lib/systemd/systemd-journald\",\"pid\":\"251\",\"start-time\":\"1565051370000000000\",\"uptime\":\"80426\"}},{\"pid\":\"256\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kauditd]\",\"pid\":\"256\",\"start-time\":\"1565051370000000000\",\"uptime\":\"80426\"}},{\"pid\":\"26\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"67\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/3]\",\"pid\":\"26\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"26553\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"10\",\"cpu-utilization\":0,\"memory-usage\":\"18616320\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"26553\",\"start-time\":\"1565122425000000000\",\"uptime\":\"9371\"}},{\"pid\":\"27\",\"state\":{\"cpu-usage-system\":\"6990\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/3]\",\"pid\":\"27\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"27123\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"18374656\",\"memory-utilization\":0,\"name\":\"/bin/bash ./clish_start\",\"pid\":\"27123\",\"start-time\":\"1565122431000000000\",\"uptime\":\"9365\"}},{\"pid\":\"27126\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"2\",\"cpu-utilization\":0,\"memory-usage\":\"28368896\",\"memory-utilization\":0,\"name\":\"/usr/sbin/cli/clish\",\"pid\":\"27126\",\"start-time\":\"1565122432000000000\",\"uptime\":\"9364\"}},{\"pid\":\"272\",\"state\":{\"cpu-usage-system\":\"54\",\"cpu-usage-user\":\"72\",\"cpu-utilization\":0,\"memory-usage\":\"47116288\",\"memory-utilization\":0,\"name\":\"/lib/systemd/systemd-udevd\",\"pid\":\"272\",\"start-time\":\"1565051370000000000\",\"uptime\":\"80426\"}},{\"pid\":\"2748\",\"state\":{\"cpu-usage-system\":\"214\",\"cpu-usage-user\":\"731\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/2319756dcf5deade9df358e6cffe9ff1374f174af811d12dc16926cd26a9f3c9 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"2748\",\"start-time\":\"1565051422000000000\",\"uptime\":\"80374\"}},{\"pid\":\"2775\",\"state\":{\"cpu-usage-system\":\"253\",\"cpu-usage-user\":\"668\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/32e64a30e3fbdd70a16964da3459176a87a492d2d75be435279e82a592d70902 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"2775\",\"start-time\":\"1565051422000000000\",\"uptime\":\"80374\"}},{\"pid\":\"28\",\"state\":{\"cpu-usage-system\":\"880\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/3]\",\"pid\":\"28\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"2803\",\"state\":{\"cpu-usage-system\":\"258\",\"cpu-usage-user\":\"667\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/91546ba72e8ed9559cca0954ed59aae984001d152dd5f919dcf4268cd94332d8 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"2803\",\"start-time\":\"1565051422000000000\",\"uptime\":\"80374\"}},{\"pid\":\"2826\",\"state\":{\"cpu-usage-system\":\"485\",\"cpu-usage-user\":\"7044\",\"cpu-utilization\":0,\"memory-usage\":\"59604992\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"2826\",\"start-time\":\"1565051422000000000\",\"uptime\":\"80374\"}},{\"pid\":\"2848\",\"state\":{\"cpu-usage-system\":\"718\",\"cpu-usage-user\":\"8459\",\"cpu-utilization\":0,\"memory-usage\":\"60043264\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"2848\",\"start-time\":\"1565051422000000000\",\"uptime\":\"80374\"}},{\"pid\":\"2861\",\"state\":{\"cpu-usage-system\":\"1032\",\"cpu-usage-user\":\"8930\",\"cpu-utilization\":0,\"memory-usage\":\"60030976\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"2861\",\"start-time\":\"1565051422000000000\",\"uptime\":\"80374\"}},{\"pid\":\"29188\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"13\",\"cpu-utilization\":0,\"memory-usage\":\"18616320\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"29188\",\"start-time\":\"1565109911000000000\",\"uptime\":\"21885\"}},{\"pid\":\"2999\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/lldp.sh wait\",\"pid\":\"2999\",\"start-time\":\"1565051423000000000\",\"uptime\":\"80373\"}},{\"pid\":\"3\",\"state\":{\"cpu-usage-system\":\"1286\",\"cpu-usage-user\":\"18\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/0]\",\"pid\":\"3\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"30\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:0H]\",\"pid\":\"30\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"3005\",\"state\":{\"cpu-usage-system\":\"115\",\"cpu-usage-user\":\"1813\",\"cpu-utilization\":0,\"memory-usage\":\"409501696\",\"memory-utilization\":0,\"name\":\"docker wait lldp\",\"pid\":\"3005\",\"start-time\":\"1565051423000000000\",\"uptime\":\"80373\"}},{\"pid\":\"3022\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"12124160\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/pmon.sh wait\",\"pid\":\"3022\",\"start-time\":\"1565051423000000000\",\"uptime\":\"80373\"}},{\"pid\":\"3029\",\"state\":{\"cpu-usage-system\":\"124\",\"cpu-usage-user\":\"1808\",\"cpu-utilization\":0,\"memory-usage\":\"544243712\",\"memory-utilization\":0,\"name\":\"docker wait pmon\",\"pid\":\"3029\",\"start-time\":\"1565051423000000000\",\"uptime\":\"80373\"}},{\"pid\":\"3033\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/bgp.sh wait\",\"pid\":\"3033\",\"start-time\":\"1565051423000000000\",\"uptime\":\"80373\"}},{\"pid\":\"3037\",\"state\":{\"cpu-usage-system\":\"122\",\"cpu-usage-user\":\"1823\",\"cpu-utilization\":0,\"memory-usage\":\"476872704\",\"memory-utilization\":0,\"name\":\"docker wait bgp\",\"pid\":\"3037\",\"start-time\":\"1565051423000000000\",\"uptime\":\"80373\"}},{\"pid\":\"31\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kdevtmpfs]\",\"pid\":\"31\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"32\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[netns]\",\"pid\":\"32\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"33\",\"state\":{\"cpu-usage-system\":\"17\",\"cpu-usage-user\":\"5\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[khungtaskd]\",\"pid\":\"33\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"34\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[oom_reaper]\",\"pid\":\"34\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"35\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[writeback]\",\"pid\":\"35\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"3565\",\"state\":{\"cpu-usage-system\":\"56\",\"cpu-usage-user\":\"43\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"3565\",\"start-time\":\"1565051430000000000\",\"uptime\":\"80366\"}},{\"pid\":\"36\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kcompactd0]\",\"pid\":\"36\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"367\",\"state\":{\"cpu-usage-system\":\"26\",\"cpu-usage-user\":\"97\",\"cpu-utilization\":0,\"memory-usage\":\"46211072\",\"memory-utilization\":0,\"name\":\"/usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation\",\"pid\":\"367\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"3702\",\"state\":{\"cpu-usage-system\":\"63\",\"cpu-usage-user\":\"34\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"3702\",\"start-time\":\"1565051432000000000\",\"uptime\":\"80364\"}},{\"pid\":\"3745\",\"state\":{\"cpu-usage-system\":\"61\",\"cpu-usage-user\":\"15\",\"cpu-utilization\":0,\"memory-usage\":\"59912192\",\"memory-utilization\":0,\"name\":\"lldpd: monitor.\",\"pid\":\"3745\",\"start-time\":\"1565051433000000000\",\"uptime\":\"80363\"}},{\"pid\":\"375\",\"state\":{\"cpu-usage-system\":\"54\",\"cpu-usage-user\":\"36\",\"cpu-utilization\":0,\"memory-usage\":\"38891520\",\"memory-utilization\":0,\"name\":\"/lib/systemd/systemd-logind\",\"pid\":\"375\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"3762\",\"state\":{\"cpu-usage-system\":\"2369\",\"cpu-usage-user\":\"3164\",\"cpu-utilization\":0,\"memory-usage\":\"59912192\",\"memory-utilization\":0,\"name\":\"lldpd: 3 neighbors.\",\"pid\":\"3762\",\"start-time\":\"1565051433000000000\",\"uptime\":\"80363\"}},{\"pid\":\"38\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksmd]\",\"pid\":\"38\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"385\",\"state\":{\"cpu-usage-system\":\"34\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"30986240\",\"memory-utilization\":0,\"name\":\"/usr/sbin/cron -f\",\"pid\":\"385\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"39\",\"state\":{\"cpu-usage-system\":\"35\",\"cpu-usage-user\":\"8\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[khugepaged]\",\"pid\":\"39\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"397\",\"state\":{\"cpu-usage-system\":\"1846\",\"cpu-usage-user\":\"4027\",\"cpu-utilization\":0,\"memory-usage\":\"1350033408\",\"memory-utilization\":0,\"name\":\"/usr/bin/containerd\",\"pid\":\"397\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"40\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[crypto]\",\"pid\":\"40\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"4008\",\"state\":{\"cpu-usage-system\":\"9154\",\"cpu-usage-user\":\"41770\",\"cpu-utilization\":0,\"memory-usage\":\"123453440\",\"memory-utilization\":0,\"name\":\"python2 -m lldp_syncd\",\"pid\":\"4008\",\"start-time\":\"1565051436000000000\",\"uptime\":\"80360\"}},{\"pid\":\"41\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kintegrityd]\",\"pid\":\"41\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"411\",\"state\":{\"cpu-usage-system\":\"3615\",\"cpu-usage-user\":\"15553\",\"cpu-utilization\":0,\"memory-usage\":\"682860544\",\"memory-utilization\":0,\"name\":\"/usr/bin/dockerd -H unix:// --storage-driver=overlay2 --bip=240.127.1.1/24 --iptables=false\",\"pid\":\"411\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"415\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bond0]\",\"pid\":\"415\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"42\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"42\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"4276\",\"state\":{\"cpu-usage-system\":\"64\",\"cpu-usage-user\":\"44\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"4276\",\"start-time\":\"1565051439000000000\",\"uptime\":\"80357\"}},{\"pid\":\"43\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kblockd]\",\"pid\":\"43\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"4313\",\"state\":{\"cpu-usage-system\":\"67\",\"cpu-usage-user\":\"110\",\"cpu-utilization\":0,\"memory-usage\":\"51687424\",\"memory-utilization\":0,\"name\":\"python /usr/bin/lldpmgrd\",\"pid\":\"4313\",\"start-time\":\"1565051440000000000\",\"uptime\":\"80356\"}},{\"pid\":\"432\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"13565952\",\"memory-utilization\":0,\"name\":\"/usr/sbin/mcelog --daemon\",\"pid\":\"432\",\"start-time\":\"1565051372000000000\",\"uptime\":\"80424\"}},{\"pid\":\"4348\",\"state\":{\"cpu-usage-system\":\"8\",\"cpu-usage-user\":\"39\",\"cpu-utilization\":0,\"memory-usage\":\"46653440\",\"memory-utilization\":0,\"name\":\"python /usr/bin/bgpcfgd\",\"pid\":\"4348\",\"start-time\":\"1565051441000000000\",\"uptime\":\"80355\"}},{\"pid\":\"44\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[devfreq_wq]\",\"pid\":\"44\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"45\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdogd]\",\"pid\":\"45\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"452\",\"state\":{\"cpu-usage-system\":\"1712\",\"cpu-usage-user\":\"579\",\"cpu-utilization\":0,\"memory-usage\":\"41881600\",\"memory-utilization\":0,\"name\":\"/usr/bin/monit -c /etc/monit/monitrc\",\"pid\":\"452\",\"start-time\":\"1565051373000000000\",\"uptime\":\"80423\"}},{\"pid\":\"4550\",\"state\":{\"cpu-usage-system\":\"194\",\"cpu-usage-user\":\"184\",\"cpu-utilization\":0,\"memory-usage\":\"572829696\",\"memory-utilization\":0,\"name\":\"/usr/lib/frr/zebra -A 127.0.0.1 -s 90000000 -M fpm -M snmp\",\"pid\":\"4550\",\"start-time\":\"1565051443000000000\",\"uptime\":\"80353\"}},{\"pid\":\"4612\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"12\",\"cpu-utilization\":0,\"memory-usage\":\"95907840\",\"memory-utilization\":0,\"name\":\"/usr/lib/frr/staticd -A 127.0.0.1\",\"pid\":\"4612\",\"start-time\":\"1565051444000000000\",\"uptime\":\"80352\"}},{\"pid\":\"47\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kswapd0]\",\"pid\":\"47\",\"start-time\":\"1565051367000000000\",\"uptime\":\"80429\"}},{\"pid\":\"479\",\"state\":{\"cpu-usage-system\":\"12\",\"cpu-usage-user\":\"4\",\"cpu-utilization\":0,\"memory-usage\":\"71630848\",\"memory-utilization\":0,\"name\":\"/usr/sbin/sshd -D\",\"pid\":\"479\",\"start-time\":\"1565051374000000000\",\"uptime\":\"80422\"}},{\"pid\":\"48\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[vmstat]\",\"pid\":\"48\",\"start-time\":\"1565051367000000000\",\"uptime\":\"80429\"}},{\"pid\":\"4831\",\"state\":{\"cpu-usage-system\":\"1284\",\"cpu-usage-user\":\"594\",\"cpu-utilization\":0,\"memory-usage\":\"359059456\",\"memory-utilization\":0,\"name\":\"/usr/lib/frr/bgpd -A 127.0.0.1 -M snmp\",\"pid\":\"4831\",\"start-time\":\"1565051447000000000\",\"uptime\":\"80349\"}},{\"pid\":\"4880\",\"state\":{\"cpu-usage-system\":\"13229\",\"cpu-usage-user\":\"25583\",\"cpu-utilization\":0,\"memory-usage\":\"143773696\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/xcvrd\",\"pid\":\"4880\",\"start-time\":\"1565051447000000000\",\"uptime\":\"80349\"}},{\"pid\":\"4981\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"103796736\",\"memory-utilization\":0,\"name\":\"fpmsyncd\",\"pid\":\"4981\",\"start-time\":\"1565051449000000000\",\"uptime\":\"80347\"}},{\"pid\":\"5\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:0H]\",\"pid\":\"5\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"5044\",\"state\":{\"cpu-usage-system\":\"8813\",\"cpu-usage-user\":\"16970\",\"cpu-utilization\":0,\"memory-usage\":\"51544064\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/psud\",\"pid\":\"5044\",\"start-time\":\"1565051450000000000\",\"uptime\":\"80346\"}},{\"pid\":\"5252\",\"state\":{\"cpu-usage-system\":\"4658\",\"cpu-usage-user\":\"11274\",\"cpu-utilization\":0,\"memory-usage\":\"73310208\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/syseepromd\",\"pid\":\"5252\",\"start-time\":\"1565051453000000000\",\"uptime\":\"80343\"}},{\"pid\":\"5777\",\"state\":{\"cpu-usage-system\":\"10\",\"cpu-usage-user\":\"15\",\"cpu-utilization\":0,\"memory-usage\":\"18624512\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"5777\",\"start-time\":\"1565113309000000000\",\"uptime\":\"18487\"}},{\"pid\":\"60\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kthrotld]\",\"pid\":\"60\",\"start-time\":\"1565051367000000000\",\"uptime\":\"80429\"}},{\"pid\":\"63\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ipv6_addrconf]\",\"pid\":\"63\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"637\",\"state\":{\"cpu-usage-system\":\"314\",\"cpu-usage-user\":\"1101\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/bd1b7f71e152dc94c4d43d003140c3263bfa6b0a24ffd07ccde51dbbd9736065 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"637\",\"start-time\":\"1565051383000000000\",\"uptime\":\"80413\"}},{\"pid\":\"653\",\"state\":{\"cpu-usage-system\":\"556\",\"cpu-usage-user\":\"4183\",\"cpu-utilization\":0,\"memory-usage\":\"59572224\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"653\",\"start-time\":\"1565051384000000000\",\"uptime\":\"80412\"}},{\"pid\":\"7\",\"state\":{\"cpu-usage-system\":\"37415\",\"cpu-usage-user\":\"68\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[rcu_sched]\",\"pid\":\"7\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"8\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[rcu_bh]\",\"pid\":\"8\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"8024\",\"state\":{\"cpu-usage-system\":\"40\",\"cpu-usage-user\":\"30\",\"cpu-utilization\":0,\"memory-usage\":\"20840448\",\"memory-utilization\":0,\"name\":\"/sbin/dhclient -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases eth0\",\"pid\":\"8024\",\"start-time\":\"1565051482000000000\",\"uptime\":\"80314\"}},{\"pid\":\"838\",\"state\":{\"cpu-usage-system\":\"59\",\"cpu-usage-user\":\"37\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"838\",\"start-time\":\"1565051388000000000\",\"uptime\":\"80408\"}},{\"pid\":\"839\",\"state\":{\"cpu-usage-system\":\"144196\",\"cpu-usage-user\":\"437478\",\"cpu-utilization\":0,\"memory-usage\":\"82518016\",\"memory-utilization\":0,\"name\":\"/usr/bin/redis-server 127.0.0.1:6379\",\"pid\":\"839\",\"start-time\":\"1565051388000000000\",\"uptime\":\"80408\"}},{\"pid\":\"9\",\"state\":{\"cpu-usage-system\":\"7954\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/0]\",\"pid\":\"9\",\"start-time\":\"1565051366000000000\",\"uptime\":\"80430\"}},{\"pid\":\"9023\",\"state\":{\"cpu-usage-system\":\"7\",\"cpu-usage-user\":\"12\",\"cpu-utilization\":0,\"memory-usage\":\"18624512\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"9023\",\"start-time\":\"1565115979000000000\",\"uptime\":\"15817\"}},{\"pid\":\"94\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[acpi_thermal_pm]\",\"pid\":\"94\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"95\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ata_sff]\",\"pid\":\"95\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"96\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_eh_0]\",\"pid\":\"96\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"97\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_tmf_0]\",\"pid\":\"97\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"98\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_eh_1]\",\"pid\":\"98\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"99\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_tmf_1]\",\"pid\":\"99\",\"start-time\":\"1565051368000000000\",\"uptime\":\"80428\"}},{\"pid\":\"994\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12136448\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/database.sh wait\",\"pid\":\"994\",\"start-time\":\"1565051391000000000\",\"uptime\":\"80405\"}},{\"pid\":\"996\",\"state\":{\"cpu-usage-system\":\"985\",\"cpu-usage-user\":\"1016\",\"cpu-utilization\":0,\"memory-usage\":\"544243712\",\"memory-utilization\":0,\"name\":\"docker wait database\",\"pid\":\"996\",\"start-time\":\"1565051391000000000\",\"uptime\":\"80405\"}}]}}} diff --git a/src/translib/test/system/02_get_system_state_command.txt b/src/translib/test/system/02_get_system_state_command.txt new file mode 100644 index 0000000000..d9f2a6c904 --- /dev/null +++ b/src/translib/test/system/02_get_system_state_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-system:system/state -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/02_get_system_state_response.json b/src/translib/test/system/02_get_system_state_response.json new file mode 100644 index 0000000000..7e59fd65a7 --- /dev/null +++ b/src/translib/test/system/02_get_system_state_response.json @@ -0,0 +1 @@ +{\"openconfig-system:state\":{\"boot-time\":\"1565051365\",\"current-datetime\":\"2019-08-06T22:55:26Z+00:00\",\"hostname\":\"sonic\"}} diff --git a/src/translib/test/system/03_get_system_memory_command.txt b/src/translib/test/system/03_get_system_memory_command.txt new file mode 100644 index 0000000000..6c71cbb836 --- /dev/null +++ b/src/translib/test/system/03_get_system_memory_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-system:system/memory -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/03_get_system_memory_response.json b/src/translib/test/system/03_get_system_memory_response.json new file mode 100644 index 0000000000..8b40d7a873 --- /dev/null +++ b/src/translib/test/system/03_get_system_memory_response.json @@ -0,0 +1 @@ +{\"openconfig-system:memory\":{\"state\":{\"physical\":\"8162872\",\"reserved\":\"1332456\"}}} diff --git a/src/translib/test/system/04_get_system_cpus_command.txt b/src/translib/test/system/04_get_system_cpus_command.txt new file mode 100644 index 0000000000..2834baf627 --- /dev/null +++ b/src/translib/test/system/04_get_system_cpus_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-system:system/cpus -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/04_get_system_cpus_response.json b/src/translib/test/system/04_get_system_cpus_response.json new file mode 100644 index 0000000000..91bdb1de59 --- /dev/null +++ b/src/translib/test/system/04_get_system_cpus_response.json @@ -0,0 +1 @@ +{\"openconfig-system:cpus\":{\"cpu\":[{\"index\":0,\"state\":{\"idle\":{\"instant\":62},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}},{\"index\":1,\"state\":{\"idle\":{\"instant\":62},\"kernel\":{\"instant\":17},\"user\":{\"instant\":16}}},{\"index\":2,\"state\":{\"idle\":{\"instant\":62},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}},{\"index\":3,\"state\":{\"idle\":{\"instant\":63},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}},{\"index\":4,\"state\":{\"idle\":{\"instant\":63},\"kernel\":{\"instant\":17},\"user\":{\"instant\":17}}}]}} diff --git a/src/translib/test/system/05_get_system_processes_command.txt b/src/translib/test/system/05_get_system_processes_command.txt new file mode 100644 index 0000000000..37c1997ce7 --- /dev/null +++ b/src/translib/test/system/05_get_system_processes_command.txt @@ -0,0 +1 @@ + ./gnmi_get -xpath /openconfig-system:system/processes -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/05_get_system_processes_response.json b/src/translib/test/system/05_get_system_processes_response.json new file mode 100644 index 0000000000..cec52dd3ce --- /dev/null +++ b/src/translib/test/system/05_get_system_processes_response.json @@ -0,0 +1 @@ +{\"openconfig-system:processes\":{\"process\":[{\"pid\":\"1\",\"state\":{\"cpu-usage-system\":\"601\",\"cpu-usage-user\":\"542\",\"cpu-utilization\":0,\"memory-usage\":\"58884096\",\"memory-utilization\":0,\"name\":\"/sbin/init\",\"pid\":\"1\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"10\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[lru-add-drain]\",\"pid\":\"10\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"10103\",\"state\":{\"cpu-usage-system\":\"302\",\"cpu-usage-user\":\"750\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/cf3ef3c803b8b239b51280a696d30205ec7286b12e7ac7ef5ea5921e305957a0 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"10103\",\"start-time\":\"1565051498000000000\",\"uptime\":\"81192\"}},{\"pid\":\"10148\",\"state\":{\"cpu-usage-system\":\"1047\",\"cpu-usage-user\":\"18811\",\"cpu-utilization\":0,\"memory-usage\":\"60203008\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"10148\",\"start-time\":\"1565051498000000000\",\"uptime\":\"81192\"}},{\"pid\":\"10487\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"12124160\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/local/bin/swss.sh wait\",\"pid\":\"10487\",\"start-time\":\"1565051501000000000\",\"uptime\":\"81189\"}},{\"pid\":\"105\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"105\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"10545\",\"state\":{\"cpu-usage-system\":\"6\",\"cpu-usage-user\":\"32\",\"cpu-utilization\":0,\"memory-usage\":\"42135552\",\"memory-utilization\":0,\"name\":\"python /usr/bin/supervisor-proc-exit-listener\",\"pid\":\"10545\",\"start-time\":\"1565051501000000000\",\"uptime\":\"81189\"}},{\"pid\":\"107\",\"state\":{\"cpu-usage-system\":\"144\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:1H]\",\"pid\":\"107\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"108\",\"state\":{\"cpu-usage-system\":\"150\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:1H]\",\"pid\":\"108\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"10822\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"46514176\",\"memory-utilization\":0,\"name\":\"sudo -i\",\"pid\":\"10822\",\"start-time\":\"1565116326000000000\",\"uptime\":\"16364\"}},{\"pid\":\"10825\",\"state\":{\"cpu-usage-system\":\"26\",\"cpu-usage-user\":\"95\",\"cpu-utilization\":0,\"memory-usage\":\"24023040\",\"memory-utilization\":0,\"name\":\"-bash\",\"pid\":\"10825\",\"start-time\":\"1565116326000000000\",\"uptime\":\"16364\"}},{\"pid\":\"109\",\"state\":{\"cpu-usage-system\":\"124\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:1H]\",\"pid\":\"109\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"11\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"66\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/0]\",\"pid\":\"11\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"110\",\"state\":{\"cpu-usage-system\":\"164\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:1H]\",\"pid\":\"110\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"11204\",\"state\":{\"cpu-usage-system\":\"94\",\"cpu-usage-user\":\"51\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"11204\",\"start-time\":\"1565051513000000000\",\"uptime\":\"81177\"}},{\"pid\":\"11298\",\"state\":{\"cpu-usage-system\":\"257\",\"cpu-usage-user\":\"650\",\"cpu-utilization\":0,\"memory-usage\":\"109920256\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/82d6d3d26404f8b674a0a2492d77958e34e5c6e9c882672c0987d3e845afa9ea -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11298\",\"start-time\":\"1565051514000000000\",\"uptime\":\"81176\"}},{\"pid\":\"11353\",\"state\":{\"cpu-usage-system\":\"436\",\"cpu-usage-user\":\"5952\",\"cpu-utilization\":0,\"memory-usage\":\"59600896\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11353\",\"start-time\":\"1565051515000000000\",\"uptime\":\"81175\"}},{\"pid\":\"11370\",\"state\":{\"cpu-usage-system\":\"284\",\"cpu-usage-user\":\"632\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/43e3fcfd19714d24ce025c1fb295633e4e5ec8373aeaa6d874d20410b4c13d70 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11370\",\"start-time\":\"1565051515000000000\",\"uptime\":\"81175\"}},{\"pid\":\"11420\",\"state\":{\"cpu-usage-system\":\"283\",\"cpu-usage-user\":\"651\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/bf414527b1d6ae6537d3fe484bd98648bdd22af80d1c893f3a6981a0df91ae9a -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11420\",\"start-time\":\"1565051515000000000\",\"uptime\":\"81175\"}},{\"pid\":\"11430\",\"state\":{\"cpu-usage-system\":\"999\",\"cpu-usage-user\":\"954\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/176d1ac359df0611c615f0657280646398744e43ee251c75d950206f175e706d -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"11430\",\"start-time\":\"1565051516000000000\",\"uptime\":\"81174\"}},{\"pid\":\"11488\",\"state\":{\"cpu-usage-system\":\"512\",\"cpu-usage-user\":\"4657\",\"cpu-utilization\":0,\"memory-usage\":\"59596800\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11488\",\"start-time\":\"1565051516000000000\",\"uptime\":\"81174\"}},{\"pid\":\"11533\",\"state\":{\"cpu-usage-system\":\"514\",\"cpu-usage-user\":\"6262\",\"cpu-utilization\":0,\"memory-usage\":\"61124608\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11533\",\"start-time\":\"1565051516000000000\",\"uptime\":\"81174\"}},{\"pid\":\"11543\",\"state\":{\"cpu-usage-system\":\"626\",\"cpu-usage-user\":\"4868\",\"cpu-utilization\":0,\"memory-usage\":\"58257408\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"11543\",\"start-time\":\"1565051516000000000\",\"uptime\":\"81174\"}},{\"pid\":\"11588\",\"state\":{\"cpu-usage-system\":\"3042\",\"cpu-usage-user\":\"20850\",\"cpu-utilization\":0,\"memory-usage\":\"188993536\",\"memory-utilization\":0,\"name\":\"/usr/bin/orchagent -d /var/log/swss -b 8192 -m 90:b1:1c:f4:a8:e1\",\"pid\":\"11588\",\"start-time\":\"1565051517000000000\",\"uptime\":\"81173\"}},{\"pid\":\"11685\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/teamd.sh wait\",\"pid\":\"11685\",\"start-time\":\"1565051518000000000\",\"uptime\":\"81172\"}},{\"pid\":\"11687\",\"state\":{\"cpu-usage-system\":\"123\",\"cpu-usage-user\":\"1832\",\"cpu-utilization\":0,\"memory-usage\":\"409763840\",\"memory-utilization\":0,\"name\":\"docker wait teamd\",\"pid\":\"11687\",\"start-time\":\"1565051518000000000\",\"uptime\":\"81172\"}},{\"pid\":\"11779\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/radv.sh wait\",\"pid\":\"11779\",\"start-time\":\"1565051519000000000\",\"uptime\":\"81171\"}},{\"pid\":\"11783\",\"state\":{\"cpu-usage-system\":\"122\",\"cpu-usage-user\":\"1847\",\"cpu-utilization\":0,\"memory-usage\":\"410025984\",\"memory-utilization\":0,\"name\":\"docker wait radv\",\"pid\":\"11783\",\"start-time\":\"1565051519000000000\",\"uptime\":\"81171\"}},{\"pid\":\"11825\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12115968\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/mgmt-framework.sh wait\",\"pid\":\"11825\",\"start-time\":\"1565051520000000000\",\"uptime\":\"81170\"}},{\"pid\":\"11829\",\"state\":{\"cpu-usage-system\":\"115\",\"cpu-usage-user\":\"1832\",\"cpu-utilization\":0,\"memory-usage\":\"476872704\",\"memory-utilization\":0,\"name\":\"docker wait mgmt-framework\",\"pid\":\"11829\",\"start-time\":\"1565051520000000000\",\"uptime\":\"81170\"}},{\"pid\":\"12\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/0]\",\"pid\":\"12\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"12113\",\"state\":{\"cpu-usage-system\":\"50\",\"cpu-usage-user\":\"46\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"12113\",\"start-time\":\"1565051526000000000\",\"uptime\":\"81164\"}},{\"pid\":\"12159\",\"state\":{\"cpu-usage-system\":\"151\",\"cpu-usage-user\":\"87\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"12159\",\"start-time\":\"1565051526000000000\",\"uptime\":\"81164\"}},{\"pid\":\"12262\",\"state\":{\"cpu-usage-system\":\"57\",\"cpu-usage-user\":\"38\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"12262\",\"start-time\":\"1565051528000000000\",\"uptime\":\"81162\"}},{\"pid\":\"12370\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"20779008\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/snmp.sh wait\",\"pid\":\"12370\",\"start-time\":\"1565051529000000000\",\"uptime\":\"81161\"}},{\"pid\":\"12374\",\"state\":{\"cpu-usage-system\":\"1102\",\"cpu-usage-user\":\"966\",\"cpu-utilization\":0,\"memory-usage\":\"474906624\",\"memory-utilization\":0,\"name\":\"docker wait snmp\",\"pid\":\"12374\",\"start-time\":\"1565051529000000000\",\"uptime\":\"81161\"}},{\"pid\":\"12385\",\"state\":{\"cpu-usage-system\":\"253\",\"cpu-usage-user\":\"678\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/bc404b0673c5df675659fa32a2fceda2fefa2f8f51e42be622378f9af58d73c3 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"12385\",\"start-time\":\"1565051529000000000\",\"uptime\":\"81161\"}},{\"pid\":\"12459\",\"state\":{\"cpu-usage-system\":\"497\",\"cpu-usage-user\":\"3923\",\"cpu-utilization\":0,\"memory-usage\":\"59596800\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"12459\",\"start-time\":\"1565051530000000000\",\"uptime\":\"81160\"}},{\"pid\":\"12477\",\"state\":{\"cpu-usage-system\":\"1126\",\"cpu-usage-user\":\"53950\",\"cpu-utilization\":0,\"memory-usage\":\"1176498176\",\"memory-utilization\":1,\"name\":\"/usr/sbin/rest_server -ui /rest_ui -logtostderr -cert /tmp/cert.pem -key /tmp/key.pem\",\"pid\":\"12477\",\"start-time\":\"1565051530000000000\",\"uptime\":\"81160\"}},{\"pid\":\"12578\",\"state\":{\"cpu-usage-system\":\"281\",\"cpu-usage-user\":\"190\",\"cpu-utilization\":0,\"memory-usage\":\"102764544\",\"memory-utilization\":0,\"name\":\"/usr/bin/teammgrd\",\"pid\":\"12578\",\"start-time\":\"1565051531000000000\",\"uptime\":\"81159\"}},{\"pid\":\"12638\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/dhcp_relay.sh wait\",\"pid\":\"12638\",\"start-time\":\"1565051532000000000\",\"uptime\":\"81158\"}},{\"pid\":\"12640\",\"state\":{\"cpu-usage-system\":\"128\",\"cpu-usage-user\":\"1848\",\"cpu-utilization\":0,\"memory-usage\":\"476610560\",\"memory-utilization\":0,\"name\":\"docker wait dhcp_relay\",\"pid\":\"12640\",\"start-time\":\"1565051532000000000\",\"uptime\":\"81158\"}},{\"pid\":\"12643\",\"state\":{\"cpu-usage-system\":\"176884\",\"cpu-usage-user\":\"111532\",\"cpu-utilization\":0,\"memory-usage\":\"102756352\",\"memory-utilization\":0,\"name\":\"/usr/bin/portsyncd -p /usr/share/sonic/hwsku/port_config.ini\",\"pid\":\"12643\",\"start-time\":\"1565051532000000000\",\"uptime\":\"81158\"}},{\"pid\":\"12885\",\"state\":{\"cpu-usage-system\":\"266\",\"cpu-usage-user\":\"195\",\"cpu-utilization\":0,\"memory-usage\":\"111271936\",\"memory-utilization\":0,\"name\":\"/usr/bin/teamsyncd\",\"pid\":\"12885\",\"start-time\":\"1565051537000000000\",\"uptime\":\"81153\"}},{\"pid\":\"13\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/1]\",\"pid\":\"13\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"13092\",\"state\":{\"cpu-usage-system\":\"246\",\"cpu-usage-user\":\"683\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/7af68f8a31ef66afb620aa861b93a017988a73696ff7ae876df9da8181f57125 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"13092\",\"start-time\":\"1565051540000000000\",\"uptime\":\"81150\"}},{\"pid\":\"13108\",\"state\":{\"cpu-usage-system\":\"404\",\"cpu-usage-user\":\"704\",\"cpu-utilization\":0,\"memory-usage\":\"102719488\",\"memory-utilization\":0,\"name\":\"/usr/bin/neighsyncd\",\"pid\":\"13108\",\"start-time\":\"1565051540000000000\",\"uptime\":\"81150\"}},{\"pid\":\"13110\",\"state\":{\"cpu-usage-system\":\"482\",\"cpu-usage-user\":\"5584\",\"cpu-utilization\":0,\"memory-usage\":\"59600896\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"13110\",\"start-time\":\"1565051540000000000\",\"uptime\":\"81150\"}},{\"pid\":\"13171\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12152832\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/local/bin/syncd.sh wait\",\"pid\":\"13171\",\"start-time\":\"1565051541000000000\",\"uptime\":\"81149\"}},{\"pid\":\"13173\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"12140544\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/swss.sh wait\",\"pid\":\"13173\",\"start-time\":\"1565051541000000000\",\"uptime\":\"81149\"}},{\"pid\":\"13175\",\"state\":{\"cpu-usage-system\":\"119\",\"cpu-usage-user\":\"1844\",\"cpu-utilization\":0,\"memory-usage\":\"409763840\",\"memory-utilization\":0,\"name\":\"docker wait swss\",\"pid\":\"13175\",\"start-time\":\"1565051541000000000\",\"uptime\":\"81149\"}},{\"pid\":\"13177\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12115968\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/syncd.sh wait\",\"pid\":\"13177\",\"start-time\":\"1565051541000000000\",\"uptime\":\"81149\"}},{\"pid\":\"13179\",\"state\":{\"cpu-usage-system\":\"132\",\"cpu-usage-user\":\"1851\",\"cpu-utilization\":0,\"memory-usage\":\"477134848\",\"memory-utilization\":0,\"name\":\"docker wait syncd\",\"pid\":\"13179\",\"start-time\":\"1565051541000000000\",\"uptime\":\"81149\"}},{\"pid\":\"1323\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"15482880\",\"memory-utilization\":0,\"name\":\"/sbin/agetty --noclear tty1 linux\",\"pid\":\"1323\",\"start-time\":\"1565051404000000000\",\"uptime\":\"81286\"}},{\"pid\":\"13389\",\"state\":{\"cpu-usage-system\":\"609\",\"cpu-usage-user\":\"210\",\"cpu-utilization\":0,\"memory-usage\":\"100241408\",\"memory-utilization\":0,\"name\":\"/usr/sbin/ntpd -p /var/run/ntpd.pid -x -u 106:110\",\"pid\":\"13389\",\"start-time\":\"1565051544000000000\",\"uptime\":\"81146\"}},{\"pid\":\"13656\",\"state\":{\"cpu-usage-system\":\"893\",\"cpu-usage-user\":\"38\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:2]\",\"pid\":\"13656\",\"start-time\":\"1565116353000000000\",\"uptime\":\"16337\"}},{\"pid\":\"13739\",\"state\":{\"cpu-usage-system\":\"70\",\"cpu-usage-user\":\"52\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"13739\",\"start-time\":\"1565051549000000000\",\"uptime\":\"81141\"}},{\"pid\":\"1381\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"14\",\"cpu-utilization\":0,\"memory-usage\":\"18616320\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"1381\",\"start-time\":\"1565054068000000000\",\"uptime\":\"78622\"}},{\"pid\":\"13971\",\"state\":{\"cpu-usage-system\":\"51\",\"cpu-usage-user\":\"43\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"13971\",\"start-time\":\"1565051552000000000\",\"uptime\":\"81138\"}},{\"pid\":\"14\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"70\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/1]\",\"pid\":\"14\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"14031\",\"state\":{\"cpu-usage-system\":\"262\",\"cpu-usage-user\":\"196\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"14031\",\"start-time\":\"1565051553000000000\",\"uptime\":\"81137\"}},{\"pid\":\"14035\",\"state\":{\"cpu-usage-system\":\"262\",\"cpu-usage-user\":\"194\",\"cpu-utilization\":0,\"memory-usage\":\"102875136\",\"memory-utilization\":0,\"name\":\"/usr/bin/vrfmgrd\",\"pid\":\"14035\",\"start-time\":\"1565051553000000000\",\"uptime\":\"81137\"}},{\"pid\":\"14046\",\"state\":{\"cpu-usage-system\":\"3\",\"cpu-usage-user\":\"4\",\"cpu-utilization\":0,\"memory-usage\":\"110243840\",\"memory-utilization\":0,\"name\":\"/usr/bin/dsserve /usr/bin/syncd --diag -u -p /etc/sai.d/sai.profile\",\"pid\":\"14046\",\"start-time\":\"1565051553000000000\",\"uptime\":\"81137\"}},{\"pid\":\"14301\",\"state\":{\"cpu-usage-system\":\"301684\",\"cpu-usage-user\":\"1871768\",\"cpu-utilization\":0,\"memory-usage\":\"1759907840\",\"memory-utilization\":3,\"name\":\"/usr/bin/syncd --diag -u -p /etc/sai.d/sai.profile\",\"pid\":\"14301\",\"start-time\":\"1565051557000000000\",\"uptime\":\"81133\"}},{\"pid\":\"14336\",\"state\":{\"cpu-usage-system\":\"1056\",\"cpu-usage-user\":\"766\",\"cpu-utilization\":0,\"memory-usage\":\"56475648\",\"memory-utilization\":0,\"name\":\"/usr/sbin/snmpd -f -LS4d -u Debian-snmp -g Debian-snmp -I -smux mteTrigger mteTriggerConf ifTable ifXTable inetCidrRouteTable ipCidrRouteTable ip disk_hw -p /run/snmpd.pid\",\"pid\":\"14336\",\"start-time\":\"1565051557000000000\",\"uptime\":\"81133\"}},{\"pid\":\"14399\",\"state\":{\"cpu-usage-system\":\"267\",\"cpu-usage-user\":\"194\",\"cpu-utilization\":0,\"memory-usage\":\"102780928\",\"memory-utilization\":0,\"name\":\"/usr/bin/vlanmgrd\",\"pid\":\"14399\",\"start-time\":\"1565051558000000000\",\"uptime\":\"81132\"}},{\"pid\":\"14545\",\"state\":{\"cpu-usage-system\":\"104094\",\"cpu-usage-user\":\"957152\",\"cpu-utilization\":0,\"memory-usage\":\"165548032\",\"memory-utilization\":0,\"name\":\"python3.6 -m sonic_ax_impl\",\"pid\":\"14545\",\"start-time\":\"1565051560000000000\",\"uptime\":\"81130\"}},{\"pid\":\"14666\",\"state\":{\"cpu-usage-system\":\"259\",\"cpu-usage-user\":\"218\",\"cpu-utilization\":0,\"memory-usage\":\"102862848\",\"memory-utilization\":0,\"name\":\"/usr/bin/intfmgrd\",\"pid\":\"14666\",\"start-time\":\"1565051561000000000\",\"uptime\":\"81129\"}},{\"pid\":\"14737\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"18374656\",\"memory-utilization\":0,\"name\":\"/bin/bash ./clish_start\",\"pid\":\"14737\",\"start-time\":\"1565111083000000000\",\"uptime\":\"21607\"}},{\"pid\":\"14739\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"28368896\",\"memory-utilization\":0,\"name\":\"/usr/sbin/cli/clish\",\"pid\":\"14739\",\"start-time\":\"1565111083000000000\",\"uptime\":\"21607\"}},{\"pid\":\"14930\",\"state\":{\"cpu-usage-system\":\"275\",\"cpu-usage-user\":\"191\",\"cpu-utilization\":0,\"memory-usage\":\"102731776\",\"memory-utilization\":0,\"name\":\"/usr/bin/portmgrd\",\"pid\":\"14930\",\"start-time\":\"1565051564000000000\",\"uptime\":\"81126\"}},{\"pid\":\"15\",\"state\":{\"cpu-usage-system\":\"7621\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/1]\",\"pid\":\"15\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"15116\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u8:2]\",\"pid\":\"15116\",\"start-time\":\"1565131868000000000\",\"uptime\":\"822\"}},{\"pid\":\"15235\",\"state\":{\"cpu-usage-system\":\"288\",\"cpu-usage-user\":\"3868\",\"cpu-utilization\":0,\"memory-usage\":\"102887424\",\"memory-utilization\":0,\"name\":\"/usr/bin/buffermgrd -l /usr/share/sonic/hwsku/pg_profile_lookup.ini\",\"pid\":\"15235\",\"start-time\":\"1565051567000000000\",\"uptime\":\"81123\"}},{\"pid\":\"15776\",\"state\":{\"cpu-usage-system\":\"272\",\"cpu-usage-user\":\"180\",\"cpu-utilization\":0,\"memory-usage\":\"102793216\",\"memory-utilization\":0,\"name\":\"/usr/bin/nbrmgrd\",\"pid\":\"15776\",\"start-time\":\"1565051573000000000\",\"uptime\":\"81117\"}},{\"pid\":\"16\",\"state\":{\"cpu-usage-system\":\"1058\",\"cpu-usage-user\":\"31\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/1]\",\"pid\":\"16\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"16057\",\"state\":{\"cpu-usage-system\":\"263\",\"cpu-usage-user\":\"197\",\"cpu-utilization\":0,\"memory-usage\":\"102846464\",\"memory-utilization\":0,\"name\":\"/usr/bin/vxlanmgrd\",\"pid\":\"16057\",\"start-time\":\"1565051577000000000\",\"uptime\":\"81113\"}},{\"pid\":\"16160\",\"state\":{\"cpu-usage-system\":\"3059\",\"cpu-usage-user\":\"1432\",\"cpu-utilization\":0,\"memory-usage\":\"143773696\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/xcvrd\",\"pid\":\"16160\",\"start-time\":\"1565051578000000000\",\"uptime\":\"81112\"}},{\"pid\":\"165\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u9:0]\",\"pid\":\"165\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"16944\",\"state\":{\"cpu-usage-system\":\"3000\",\"cpu-usage-user\":\"153\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:1]\",\"pid\":\"16944\",\"start-time\":\"1565074324000000000\",\"uptime\":\"58366\"}},{\"pid\":\"174\",\"state\":{\"cpu-usage-system\":\"727\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[jbd2/sda4-8]\",\"pid\":\"174\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"175\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ext4-rsv-conver]\",\"pid\":\"175\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"179\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"179\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"17999\",\"state\":{\"cpu-usage-system\":\"42\",\"cpu-usage-user\":\"152\",\"cpu-utilization\":0,\"memory-usage\":\"24301568\",\"memory-utilization\":0,\"name\":\"-bash\",\"pid\":\"17999\",\"start-time\":\"1565113433000000000\",\"uptime\":\"19257\"}},{\"pid\":\"18\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:0H]\",\"pid\":\"18\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"181\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"181\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"18120\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/u8:1]\",\"pid\":\"18120\",\"start-time\":\"1565132228000000000\",\"uptime\":\"462\"}},{\"pid\":\"182\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"182\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"184\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"184\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"186\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"186\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"188\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"188\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"189\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"189\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"19\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/2]\",\"pid\":\"19\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"190\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"190\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"196\",\"state\":{\"cpu-usage-system\":\"198\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[loop0]\",\"pid\":\"196\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"19993\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"12\",\"cpu-utilization\":0,\"memory-usage\":\"18620416\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"19993\",\"start-time\":\"1565130933000000000\",\"uptime\":\"1757\"}},{\"pid\":\"2\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kthreadd]\",\"pid\":\"2\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"20\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"72\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/2]\",\"pid\":\"20\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"20098\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:1]\",\"pid\":\"20098\",\"start-time\":\"1565130605000000000\",\"uptime\":\"2085\"}},{\"pid\":\"20506\",\"state\":{\"cpu-usage-system\":\"938\",\"cpu-usage-user\":\"219\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:0]\",\"pid\":\"20506\",\"start-time\":\"1565052269000000000\",\"uptime\":\"80421\"}},{\"pid\":\"20820\",\"state\":{\"cpu-usage-system\":\"1684\",\"cpu-usage-user\":\"18\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:0]\",\"pid\":\"20820\",\"start-time\":\"1565117416000000000\",\"uptime\":\"15274\"}},{\"pid\":\"20903\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"69513216\",\"memory-utilization\":0,\"name\":\"/bin/login --\",\"pid\":\"20903\",\"start-time\":\"1565053934000000000\",\"uptime\":\"78756\"}},{\"pid\":\"21\",\"state\":{\"cpu-usage-system\":\"6965\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/2]\",\"pid\":\"21\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"2138\",\"state\":{\"cpu-usage-system\":\"12\",\"cpu-usage-user\":\"72\",\"cpu-utilization\":0,\"memory-usage\":\"62746624\",\"memory-utilization\":0,\"name\":\"/usr/bin/python -u /usr/bin/hostcfgd\",\"pid\":\"2138\",\"start-time\":\"1565051412000000000\",\"uptime\":\"81278\"}},{\"pid\":\"2148\",\"state\":{\"cpu-usage-system\":\"10\",\"cpu-usage-user\":\"34\",\"cpu-utilization\":0,\"memory-usage\":\"57978880\",\"memory-utilization\":0,\"name\":\"python /usr/bin/caclmgrd\",\"pid\":\"2148\",\"start-time\":\"1565051412000000000\",\"uptime\":\"81278\"}},{\"pid\":\"21950\",\"state\":{\"cpu-usage-system\":\"58\",\"cpu-usage-user\":\"102\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:0]\",\"pid\":\"21950\",\"start-time\":\"1565130293000000000\",\"uptime\":\"2397\"}},{\"pid\":\"22\",\"state\":{\"cpu-usage-system\":\"1034\",\"cpu-usage-user\":\"5\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/2]\",\"pid\":\"22\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"220\",\"state\":{\"cpu-usage-system\":\"597\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[loop1]\",\"pid\":\"220\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"22164\",\"state\":{\"cpu-usage-system\":\"8\",\"cpu-usage-user\":\"28\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/c647cc0b7005f468d6c62da90019f93df6ad6376a5fc410f85edbe438da95b17 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"22164\",\"start-time\":\"1565130294000000000\",\"uptime\":\"2396\"}},{\"pid\":\"222\",\"state\":{\"cpu-usage-system\":\"200\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[jbd2/loop1-8]\",\"pid\":\"222\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"22206\",\"state\":{\"cpu-usage-system\":\"38\",\"cpu-usage-user\":\"329\",\"cpu-utilization\":0,\"memory-usage\":\"59600896\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"22206\",\"start-time\":\"1565130294000000000\",\"uptime\":\"2396\"}},{\"pid\":\"223\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ext4-rsv-conver]\",\"pid\":\"223\",\"start-time\":\"1565051369000000000\",\"uptime\":\"81321\"}},{\"pid\":\"22312\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12115968\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/telemetry.sh wait\",\"pid\":\"22312\",\"start-time\":\"1565130295000000000\",\"uptime\":\"2395\"}},{\"pid\":\"22316\",\"state\":{\"cpu-usage-system\":\"15\",\"cpu-usage-user\":\"68\",\"cpu-utilization\":0,\"memory-usage\":\"399929344\",\"memory-utilization\":0,\"name\":\"docker wait telemetry\",\"pid\":\"22316\",\"start-time\":\"1565130295000000000\",\"uptime\":\"2395\"}},{\"pid\":\"22318\",\"state\":{\"cpu-usage-system\":\"154\",\"cpu-usage-user\":\"6\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:0]\",\"pid\":\"22318\",\"start-time\":\"1565130295000000000\",\"uptime\":\"2395\"}},{\"pid\":\"22676\",\"state\":{\"cpu-usage-system\":\"3\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"22676\",\"start-time\":\"1565130299000000000\",\"uptime\":\"2391\"}},{\"pid\":\"22849\",\"state\":{\"cpu-usage-system\":\"1448\",\"cpu-usage-user\":\"3102\",\"cpu-utilization\":0,\"memory-usage\":\"760782848\",\"memory-utilization\":0,\"name\":\"telemetry -logtostderr --insecure --port 8080 --allow_no_client_auth -v=2\",\"pid\":\"22849\",\"start-time\":\"1565130301000000000\",\"uptime\":\"2389\"}},{\"pid\":\"23170\",\"state\":{\"cpu-usage-system\":\"1501\",\"cpu-usage-user\":\"1272\",\"cpu-utilization\":0,\"memory-usage\":\"607961088\",\"memory-utilization\":0,\"name\":\"/usr/sbin/dialout_client_cli -insecure -logtostderr -v 2\",\"pid\":\"23170\",\"start-time\":\"1565130305000000000\",\"uptime\":\"2385\"}},{\"pid\":\"23200\",\"state\":{\"cpu-usage-system\":\"320\",\"cpu-usage-user\":\"27\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/1:1]\",\"pid\":\"23200\",\"start-time\":\"1565125024000000000\",\"uptime\":\"7666\"}},{\"pid\":\"2362\",\"state\":{\"cpu-usage-system\":\"344\",\"cpu-usage-user\":\"322\",\"cpu-utilization\":0,\"memory-usage\":\"329822208\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"2362\",\"start-time\":\"1565051416000000000\",\"uptime\":\"81274\"}},{\"pid\":\"24\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/2:0H]\",\"pid\":\"24\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"25\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[cpuhp/3]\",\"pid\":\"25\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"251\",\"state\":{\"cpu-usage-system\":\"426\",\"cpu-usage-user\":\"254\",\"cpu-utilization\":0,\"memory-usage\":\"45461504\",\"memory-utilization\":0,\"name\":\"/lib/systemd/systemd-journald\",\"pid\":\"251\",\"start-time\":\"1565051370000000000\",\"uptime\":\"81320\"}},{\"pid\":\"256\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kauditd]\",\"pid\":\"256\",\"start-time\":\"1565051370000000000\",\"uptime\":\"81320\"}},{\"pid\":\"26\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"68\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdog/3]\",\"pid\":\"26\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"26553\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"10\",\"cpu-utilization\":0,\"memory-usage\":\"18616320\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"26553\",\"start-time\":\"1565122425000000000\",\"uptime\":\"10265\"}},{\"pid\":\"27\",\"state\":{\"cpu-usage-system\":\"7071\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/3]\",\"pid\":\"27\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"27123\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"18374656\",\"memory-utilization\":0,\"name\":\"/bin/bash ./clish_start\",\"pid\":\"27123\",\"start-time\":\"1565122431000000000\",\"uptime\":\"10259\"}},{\"pid\":\"27126\",\"state\":{\"cpu-usage-system\":\"1\",\"cpu-usage-user\":\"2\",\"cpu-utilization\":0,\"memory-usage\":\"28368896\",\"memory-utilization\":0,\"name\":\"/usr/sbin/cli/clish\",\"pid\":\"27126\",\"start-time\":\"1565122432000000000\",\"uptime\":\"10258\"}},{\"pid\":\"272\",\"state\":{\"cpu-usage-system\":\"54\",\"cpu-usage-user\":\"72\",\"cpu-utilization\":0,\"memory-usage\":\"47116288\",\"memory-utilization\":0,\"name\":\"/lib/systemd/systemd-udevd\",\"pid\":\"272\",\"start-time\":\"1565051370000000000\",\"uptime\":\"81320\"}},{\"pid\":\"2748\",\"state\":{\"cpu-usage-system\":\"216\",\"cpu-usage-user\":\"740\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/2319756dcf5deade9df358e6cffe9ff1374f174af811d12dc16926cd26a9f3c9 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"2748\",\"start-time\":\"1565051422000000000\",\"uptime\":\"81268\"}},{\"pid\":\"2775\",\"state\":{\"cpu-usage-system\":\"254\",\"cpu-usage-user\":\"680\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/32e64a30e3fbdd70a16964da3459176a87a492d2d75be435279e82a592d70902 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"2775\",\"start-time\":\"1565051422000000000\",\"uptime\":\"81268\"}},{\"pid\":\"28\",\"state\":{\"cpu-usage-system\":\"890\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/3]\",\"pid\":\"28\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"2803\",\"state\":{\"cpu-usage-system\":\"268\",\"cpu-usage-user\":\"669\",\"cpu-utilization\":0,\"memory-usage\":\"111427584\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/91546ba72e8ed9559cca0954ed59aae984001d152dd5f919dcf4268cd94332d8 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"2803\",\"start-time\":\"1565051422000000000\",\"uptime\":\"81268\"}},{\"pid\":\"2826\",\"state\":{\"cpu-usage-system\":\"489\",\"cpu-usage-user\":\"7123\",\"cpu-utilization\":0,\"memory-usage\":\"59604992\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"2826\",\"start-time\":\"1565051422000000000\",\"uptime\":\"81268\"}},{\"pid\":\"2848\",\"state\":{\"cpu-usage-system\":\"723\",\"cpu-usage-user\":\"8555\",\"cpu-utilization\":0,\"memory-usage\":\"60043264\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"2848\",\"start-time\":\"1565051422000000000\",\"uptime\":\"81268\"}},{\"pid\":\"2861\",\"state\":{\"cpu-usage-system\":\"1048\",\"cpu-usage-user\":\"9024\",\"cpu-utilization\":0,\"memory-usage\":\"60030976\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"2861\",\"start-time\":\"1565051422000000000\",\"uptime\":\"81268\"}},{\"pid\":\"29188\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"13\",\"cpu-utilization\":0,\"memory-usage\":\"18616320\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"29188\",\"start-time\":\"1565109911000000000\",\"uptime\":\"22779\"}},{\"pid\":\"2999\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/lldp.sh wait\",\"pid\":\"2999\",\"start-time\":\"1565051423000000000\",\"uptime\":\"81267\"}},{\"pid\":\"3\",\"state\":{\"cpu-usage-system\":\"1301\",\"cpu-usage-user\":\"18\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksoftirqd/0]\",\"pid\":\"3\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"30\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/3:0H]\",\"pid\":\"30\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"3005\",\"state\":{\"cpu-usage-system\":\"118\",\"cpu-usage-user\":\"1834\",\"cpu-utilization\":0,\"memory-usage\":\"409501696\",\"memory-utilization\":0,\"name\":\"docker wait lldp\",\"pid\":\"3005\",\"start-time\":\"1565051423000000000\",\"uptime\":\"81267\"}},{\"pid\":\"3022\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"1\",\"cpu-utilization\":0,\"memory-usage\":\"12124160\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/pmon.sh wait\",\"pid\":\"3022\",\"start-time\":\"1565051423000000000\",\"uptime\":\"81267\"}},{\"pid\":\"3029\",\"state\":{\"cpu-usage-system\":\"124\",\"cpu-usage-user\":\"1830\",\"cpu-utilization\":0,\"memory-usage\":\"544243712\",\"memory-utilization\":0,\"name\":\"docker wait pmon\",\"pid\":\"3029\",\"start-time\":\"1565051423000000000\",\"uptime\":\"81267\"}},{\"pid\":\"3033\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12120064\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/bgp.sh wait\",\"pid\":\"3033\",\"start-time\":\"1565051423000000000\",\"uptime\":\"81267\"}},{\"pid\":\"3037\",\"state\":{\"cpu-usage-system\":\"124\",\"cpu-usage-user\":\"1842\",\"cpu-utilization\":0,\"memory-usage\":\"476872704\",\"memory-utilization\":0,\"name\":\"docker wait bgp\",\"pid\":\"3037\",\"start-time\":\"1565051423000000000\",\"uptime\":\"81267\"}},{\"pid\":\"31\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kdevtmpfs]\",\"pid\":\"31\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"32\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[netns]\",\"pid\":\"32\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"33\",\"state\":{\"cpu-usage-system\":\"17\",\"cpu-usage-user\":\"5\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[khungtaskd]\",\"pid\":\"33\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"34\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[oom_reaper]\",\"pid\":\"34\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"35\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[writeback]\",\"pid\":\"35\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"3565\",\"state\":{\"cpu-usage-system\":\"56\",\"cpu-usage-user\":\"44\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"3565\",\"start-time\":\"1565051430000000000\",\"uptime\":\"81260\"}},{\"pid\":\"36\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kcompactd0]\",\"pid\":\"36\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"367\",\"state\":{\"cpu-usage-system\":\"26\",\"cpu-usage-user\":\"97\",\"cpu-utilization\":0,\"memory-usage\":\"46211072\",\"memory-utilization\":0,\"name\":\"/usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation\",\"pid\":\"367\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"3702\",\"state\":{\"cpu-usage-system\":\"64\",\"cpu-usage-user\":\"34\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"3702\",\"start-time\":\"1565051432000000000\",\"uptime\":\"81258\"}},{\"pid\":\"3745\",\"state\":{\"cpu-usage-system\":\"62\",\"cpu-usage-user\":\"15\",\"cpu-utilization\":0,\"memory-usage\":\"59912192\",\"memory-utilization\":0,\"name\":\"lldpd: monitor.\",\"pid\":\"3745\",\"start-time\":\"1565051433000000000\",\"uptime\":\"81257\"}},{\"pid\":\"375\",\"state\":{\"cpu-usage-system\":\"54\",\"cpu-usage-user\":\"36\",\"cpu-utilization\":0,\"memory-usage\":\"38891520\",\"memory-utilization\":0,\"name\":\"/lib/systemd/systemd-logind\",\"pid\":\"375\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"3762\",\"state\":{\"cpu-usage-system\":\"2394\",\"cpu-usage-user\":\"3202\",\"cpu-utilization\":0,\"memory-usage\":\"59912192\",\"memory-utilization\":0,\"name\":\"lldpd: 3 neighbors.\",\"pid\":\"3762\",\"start-time\":\"1565051433000000000\",\"uptime\":\"81257\"}},{\"pid\":\"38\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ksmd]\",\"pid\":\"38\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"385\",\"state\":{\"cpu-usage-system\":\"35\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"30986240\",\"memory-utilization\":0,\"name\":\"/usr/sbin/cron -f\",\"pid\":\"385\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"39\",\"state\":{\"cpu-usage-system\":\"35\",\"cpu-usage-user\":\"8\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[khugepaged]\",\"pid\":\"39\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"397\",\"state\":{\"cpu-usage-system\":\"1862\",\"cpu-usage-user\":\"4072\",\"cpu-utilization\":0,\"memory-usage\":\"1350033408\",\"memory-utilization\":0,\"name\":\"/usr/bin/containerd\",\"pid\":\"397\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"40\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[crypto]\",\"pid\":\"40\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"4008\",\"state\":{\"cpu-usage-system\":\"9254\",\"cpu-usage-user\":\"42231\",\"cpu-utilization\":0,\"memory-usage\":\"123453440\",\"memory-utilization\":0,\"name\":\"python2 -m lldp_syncd\",\"pid\":\"4008\",\"start-time\":\"1565051436000000000\",\"uptime\":\"81254\"}},{\"pid\":\"41\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kintegrityd]\",\"pid\":\"41\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"411\",\"state\":{\"cpu-usage-system\":\"3635\",\"cpu-usage-user\":\"15627\",\"cpu-utilization\":0,\"memory-usage\":\"682860544\",\"memory-utilization\":0,\"name\":\"/usr/bin/dockerd -H unix:// --storage-driver=overlay2 --bip=240.127.1.1/24 --iptables=false\",\"pid\":\"411\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"415\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bond0]\",\"pid\":\"415\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"42\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[bioset]\",\"pid\":\"42\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"4276\",\"state\":{\"cpu-usage-system\":\"64\",\"cpu-usage-user\":\"44\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"4276\",\"start-time\":\"1565051439000000000\",\"uptime\":\"81251\"}},{\"pid\":\"43\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kblockd]\",\"pid\":\"43\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"4313\",\"state\":{\"cpu-usage-system\":\"68\",\"cpu-usage-user\":\"111\",\"cpu-utilization\":0,\"memory-usage\":\"51687424\",\"memory-utilization\":0,\"name\":\"python /usr/bin/lldpmgrd\",\"pid\":\"4313\",\"start-time\":\"1565051440000000000\",\"uptime\":\"81250\"}},{\"pid\":\"432\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"13565952\",\"memory-utilization\":0,\"name\":\"/usr/sbin/mcelog --daemon\",\"pid\":\"432\",\"start-time\":\"1565051372000000000\",\"uptime\":\"81318\"}},{\"pid\":\"4348\",\"state\":{\"cpu-usage-system\":\"8\",\"cpu-usage-user\":\"39\",\"cpu-utilization\":0,\"memory-usage\":\"46653440\",\"memory-utilization\":0,\"name\":\"python /usr/bin/bgpcfgd\",\"pid\":\"4348\",\"start-time\":\"1565051441000000000\",\"uptime\":\"81249\"}},{\"pid\":\"44\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[devfreq_wq]\",\"pid\":\"44\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"45\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[watchdogd]\",\"pid\":\"45\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"452\",\"state\":{\"cpu-usage-system\":\"1735\",\"cpu-usage-user\":\"588\",\"cpu-utilization\":0,\"memory-usage\":\"41881600\",\"memory-utilization\":0,\"name\":\"/usr/bin/monit -c /etc/monit/monitrc\",\"pid\":\"452\",\"start-time\":\"1565051373000000000\",\"uptime\":\"81317\"}},{\"pid\":\"4550\",\"state\":{\"cpu-usage-system\":\"196\",\"cpu-usage-user\":\"185\",\"cpu-utilization\":0,\"memory-usage\":\"572829696\",\"memory-utilization\":0,\"name\":\"/usr/lib/frr/zebra -A 127.0.0.1 -s 90000000 -M fpm -M snmp\",\"pid\":\"4550\",\"start-time\":\"1565051443000000000\",\"uptime\":\"81247\"}},{\"pid\":\"4612\",\"state\":{\"cpu-usage-system\":\"4\",\"cpu-usage-user\":\"12\",\"cpu-utilization\":0,\"memory-usage\":\"95907840\",\"memory-utilization\":0,\"name\":\"/usr/lib/frr/staticd -A 127.0.0.1\",\"pid\":\"4612\",\"start-time\":\"1565051444000000000\",\"uptime\":\"81246\"}},{\"pid\":\"47\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kswapd0]\",\"pid\":\"47\",\"start-time\":\"1565051367000000000\",\"uptime\":\"81323\"}},{\"pid\":\"479\",\"state\":{\"cpu-usage-system\":\"12\",\"cpu-usage-user\":\"4\",\"cpu-utilization\":0,\"memory-usage\":\"71630848\",\"memory-utilization\":0,\"name\":\"/usr/sbin/sshd -D\",\"pid\":\"479\",\"start-time\":\"1565051374000000000\",\"uptime\":\"81316\"}},{\"pid\":\"48\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[vmstat]\",\"pid\":\"48\",\"start-time\":\"1565051367000000000\",\"uptime\":\"81323\"}},{\"pid\":\"4831\",\"state\":{\"cpu-usage-system\":\"1299\",\"cpu-usage-user\":\"599\",\"cpu-utilization\":0,\"memory-usage\":\"359059456\",\"memory-utilization\":0,\"name\":\"/usr/lib/frr/bgpd -A 127.0.0.1 -M snmp\",\"pid\":\"4831\",\"start-time\":\"1565051447000000000\",\"uptime\":\"81243\"}},{\"pid\":\"4880\",\"state\":{\"cpu-usage-system\":\"13387\",\"cpu-usage-user\":\"25867\",\"cpu-utilization\":0,\"memory-usage\":\"143773696\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/xcvrd\",\"pid\":\"4880\",\"start-time\":\"1565051447000000000\",\"uptime\":\"81243\"}},{\"pid\":\"4981\",\"state\":{\"cpu-usage-system\":\"2\",\"cpu-usage-user\":\"7\",\"cpu-utilization\":0,\"memory-usage\":\"103796736\",\"memory-utilization\":0,\"name\":\"fpmsyncd\",\"pid\":\"4981\",\"start-time\":\"1565051449000000000\",\"uptime\":\"81241\"}},{\"pid\":\"5\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kworker/0:0H]\",\"pid\":\"5\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"5044\",\"state\":{\"cpu-usage-system\":\"8908\",\"cpu-usage-user\":\"17168\",\"cpu-utilization\":0,\"memory-usage\":\"51544064\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/psud\",\"pid\":\"5044\",\"start-time\":\"1565051450000000000\",\"uptime\":\"81240\"}},{\"pid\":\"5252\",\"state\":{\"cpu-usage-system\":\"4711\",\"cpu-usage-user\":\"11400\",\"cpu-utilization\":0,\"memory-usage\":\"73310208\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/syseepromd\",\"pid\":\"5252\",\"start-time\":\"1565051453000000000\",\"uptime\":\"81237\"}},{\"pid\":\"5777\",\"state\":{\"cpu-usage-system\":\"10\",\"cpu-usage-user\":\"15\",\"cpu-utilization\":0,\"memory-usage\":\"18624512\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"5777\",\"start-time\":\"1565113309000000000\",\"uptime\":\"19381\"}},{\"pid\":\"60\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[kthrotld]\",\"pid\":\"60\",\"start-time\":\"1565051367000000000\",\"uptime\":\"81323\"}},{\"pid\":\"63\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ipv6_addrconf]\",\"pid\":\"63\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"637\",\"state\":{\"cpu-usage-system\":\"317\",\"cpu-usage-user\":\"1110\",\"cpu-utilization\":0,\"memory-usage\":\"111362048\",\"memory-utilization\":0,\"name\":\"containerd-shim -namespace moby -workdir /var/lib/containerd/io.containerd.runtime.v1.linux/moby/bd1b7f71e152dc94c4d43d003140c3263bfa6b0a24ffd07ccde51dbbd9736065 -address /run/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc\",\"pid\":\"637\",\"start-time\":\"1565051383000000000\",\"uptime\":\"81307\"}},{\"pid\":\"653\",\"state\":{\"cpu-usage-system\":\"563\",\"cpu-usage-user\":\"4228\",\"cpu-utilization\":0,\"memory-usage\":\"59572224\",\"memory-utilization\":0,\"name\":\"/usr/bin/python /usr/bin/supervisord\",\"pid\":\"653\",\"start-time\":\"1565051384000000000\",\"uptime\":\"81306\"}},{\"pid\":\"7\",\"state\":{\"cpu-usage-system\":\"37867\",\"cpu-usage-user\":\"68\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[rcu_sched]\",\"pid\":\"7\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"8\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[rcu_bh]\",\"pid\":\"8\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"8024\",\"state\":{\"cpu-usage-system\":\"40\",\"cpu-usage-user\":\"30\",\"cpu-utilization\":0,\"memory-usage\":\"20840448\",\"memory-utilization\":0,\"name\":\"/sbin/dhclient -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases eth0\",\"pid\":\"8024\",\"start-time\":\"1565051482000000000\",\"uptime\":\"81208\"}},{\"pid\":\"838\",\"state\":{\"cpu-usage-system\":\"60\",\"cpu-usage-user\":\"38\",\"cpu-utilization\":0,\"memory-usage\":\"256139264\",\"memory-utilization\":0,\"name\":\"/usr/sbin/rsyslogd -n\",\"pid\":\"838\",\"start-time\":\"1565051388000000000\",\"uptime\":\"81302\"}},{\"pid\":\"839\",\"state\":{\"cpu-usage-system\":\"145822\",\"cpu-usage-user\":\"442517\",\"cpu-utilization\":0,\"memory-usage\":\"82518016\",\"memory-utilization\":0,\"name\":\"/usr/bin/redis-server 127.0.0.1:6379\",\"pid\":\"839\",\"start-time\":\"1565051388000000000\",\"uptime\":\"81302\"}},{\"pid\":\"9\",\"state\":{\"cpu-usage-system\":\"8042\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[migration/0]\",\"pid\":\"9\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81324\"}},{\"pid\":\"9023\",\"state\":{\"cpu-usage-system\":\"7\",\"cpu-usage-user\":\"12\",\"cpu-utilization\":0,\"memory-usage\":\"18624512\",\"memory-utilization\":0,\"name\":\"bash\",\"pid\":\"9023\",\"start-time\":\"1565115979000000000\",\"uptime\":\"16711\"}},{\"pid\":\"94\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[acpi_thermal_pm]\",\"pid\":\"94\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"95\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[ata_sff]\",\"pid\":\"95\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"96\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_eh_0]\",\"pid\":\"96\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"97\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_tmf_0]\",\"pid\":\"97\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"98\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_eh_1]\",\"pid\":\"98\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"99\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"0\",\"memory-utilization\":0,\"name\":\"[scsi_tmf_1]\",\"pid\":\"99\",\"start-time\":\"1565051368000000000\",\"uptime\":\"81322\"}},{\"pid\":\"994\",\"state\":{\"cpu-usage-system\":\"0\",\"cpu-usage-user\":\"0\",\"cpu-utilization\":0,\"memory-usage\":\"12136448\",\"memory-utilization\":0,\"name\":\"/bin/bash /usr/bin/database.sh wait\",\"pid\":\"994\",\"start-time\":\"1565051391000000000\",\"uptime\":\"81299\"}},{\"pid\":\"996\",\"state\":{\"cpu-usage-system\":\"998\",\"cpu-usage-user\":\"1026\",\"cpu-utilization\":0,\"memory-usage\":\"544243712\",\"memory-utilization\":0,\"name\":\"docker wait database\",\"pid\":\"996\",\"start-time\":\"1565051391000000000\",\"uptime\":\"81299\"}}]}} diff --git a/src/translib/test/system/06_get_system_processes_pid_command.txt b/src/translib/test/system/06_get_system_processes_pid_command.txt new file mode 100644 index 0000000000..8f69190e2f --- /dev/null +++ b/src/translib/test/system/06_get_system_processes_pid_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-system:system/processes/process[pid=1] -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/06_get_system_processes_pid_response.json b/src/translib/test/system/06_get_system_processes_pid_response.json new file mode 100644 index 0000000000..72cdf97c8a --- /dev/null +++ b/src/translib/test/system/06_get_system_processes_pid_response.json @@ -0,0 +1 @@ +{\"openconfig-system:process\":[{\"pid\":\"1\",\"state\":{\"cpu-usage-system\":\"601\",\"cpu-usage-user\":\"543\",\"cpu-utilization\":0,\"memory-usage\":\"58884096\",\"memory-utilization\":0,\"name\":\"/sbin/init\",\"pid\":\"1\",\"start-time\":\"1565051366000000000\",\"uptime\":\"81651\"}}]} diff --git a/src/translib/test/system/07_get_system_processes_pid_attribute_command.txt b/src/translib/test/system/07_get_system_processes_pid_attribute_command.txt new file mode 100644 index 0000000000..33a44bf636 --- /dev/null +++ b/src/translib/test/system/07_get_system_processes_pid_attribute_command.txt @@ -0,0 +1 @@ +./gnmi_get -xpath /openconfig-system:system/pid=1]/state/name -target_addr 127.0.0.1:8080 -alsologtostderr -insecure true -pretty diff --git a/src/translib/test/system/07_get_system_processes_pid_attribute_response.json b/src/translib/test/system/07_get_system_processes_pid_attribute_response.json new file mode 100644 index 0000000000..b04ef08cd9 --- /dev/null +++ b/src/translib/test/system/07_get_system_processes_pid_attribute_response.json @@ -0,0 +1 @@ +{\"openconfig-system:process\":[{\"pid\":\"1\",\"state\":{\"cpu-usage-system\":\"662\",\"cpu-usage-user\":\"638\",\"cpu-utilization\":0,\"memory-usage\":\"58761216\",\"memory-utilization\":0,\"name\":\"/sbin/init\",\"pid\":\"1\",\"start-time\":\"1563911114000000000\",\"uptime\":\"102852\"}}]} diff --git a/src/translib/test/translibtest.go b/src/translib/test/translibtest.go new file mode 100644 index 0000000000..8c47edbbba --- /dev/null +++ b/src/translib/test/translibtest.go @@ -0,0 +1,146 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + "flag" + "io/ioutil" + "translib" + "strings" + //"sync" + "github.com/Workiva/go-datastructures/queue" + log "github.com/golang/glog" +) + +func main() { + var err error + operationPtr := flag.String("o", "get", "Operation: create,update,replace,delete,get,action,getmodels,subscribe,supportsubscribe") + uriPtr := flag.String("u", "", "URI string") + payloadFilePtr := flag.String("p", "", "JSON payload file") + flag.Parse() + log.Info("operation =", *operationPtr) + log.Info("uri =", *uriPtr) + log.Info("payload =", *payloadFilePtr) + + payloadFromFile, err := ioutil.ReadFile(*payloadFilePtr) + if err != nil { + log.Fatal(err) + } + + if *operationPtr == "create" { + req := translib.SetRequest{Path:*uriPtr, Payload:[]byte(payloadFromFile)} + translib.Create(req) + } else if *operationPtr == "update" { + req := translib.SetRequest{Path:*uriPtr, Payload:[]byte(payloadFromFile)} + translib.Update(req) + } else if *operationPtr == "replace" { + req := translib.SetRequest{Path:*uriPtr, Payload:[]byte(payloadFromFile)} + translib.Replace(req) + } else if *operationPtr == "delete" { + req := translib.SetRequest{Path:*uriPtr} + translib.Delete(req) + } else if *operationPtr == "get" { + req := translib.GetRequest{Path:*uriPtr} + resp, _ := translib.Get(req) + log.Info("Response payload =", string(resp.Payload)) + } else if *operationPtr == "action" { + req := translib.ActionRequest{Path:*uriPtr} + resp, _ := translib.Action(req) + log.Info("Response payload =", string(resp.Payload)) + } else if *operationPtr == "getmodels" { + models,_ := translib.GetModels() + log.Info("Models =", models) + } else if *operationPtr == "supportsubscribe" { + paths := strings.Split(*uriPtr, ",") + log.Info("Paths =", paths) + + resp, _ := translib.IsSubscribeSupported(paths) + + for i, path := range paths { + log.Info("Response returned for path=", path) + log.Info(*(resp[i])) + } + + } else if *operationPtr == "subscribe" { + paths := strings.Split(*uriPtr, ",") + log.Info("Paths =", paths) + var q *queue.PriorityQueue + var stop chan struct{} + + q = queue.NewPriorityQueue(1, false) + stop = make(chan struct{}, 1) + translib.Subscribe(paths, q, stop) + log.Info("Subscribe completed") + for { + log.Info("Before calling Get") + items, err := q.Get(1) + log.Info("After calling Get") + + if items == nil { + log.V(1).Infof("%v", err) + break + } + if err != nil { + log.V(1).Infof("%v", err) + break + } + + resp, _ := (items[0]).(*translib.SubscribeResponse) + log.Info("SubscribeResponse received =", string(resp.Payload)) + log.Info("IsSync complete = ", resp.SyncComplete) + + if resp.SyncComplete { + break + } + } + + var q1 *queue.PriorityQueue + var stop1 chan struct{} + + q1 = queue.NewPriorityQueue(1, false) + stop1 = make(chan struct{}, 1) + translib.Subscribe(paths, q1, stop1) + log.Info("Subscribe completed") + for { + log.Info("Before calling Get") + items, err := q1.Get(1) + log.Info("After calling Get") + + if items == nil { + log.V(1).Infof("%v", err) + break + } + if err != nil { + log.V(1).Infof("%v", err) + break + } + + resp, _ := (items[0]).(*translib.SubscribeResponse) + log.Info("SubscribeResponse received =", string(resp.Payload)) + log.Info("IsSync complete = ", resp.SyncComplete) + + if resp.SyncComplete { + } + } + + } else { + log.Info("Invalid Operation") + } +} diff --git a/src/translib/tlerr/app_errors.go b/src/translib/tlerr/app_errors.go new file mode 100644 index 0000000000..d695954326 --- /dev/null +++ b/src/translib/tlerr/app_errors.go @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 + +// "app errors" are used to return user displayable error messages +// to NB agents. + +// errordata holds user displayable message template, arguments +// and optional path string. +type errordata struct { + Format string // message format string + Args []interface{} // message format arguments + Path string // error path (optional) +} + +// InvalidArgsError indicates bad request error. +type InvalidArgsError errordata + +// NotFoundError indicates resource not found error. +type NotFoundError errordata + +// AlreadyExistsError indicates resource exists error. +type AlreadyExistsError errordata + +// NotSupportedError indicates unspported operation error. +type NotSupportedError errordata + +// InternalError indicates a generic error during app execution. +type InternalError errordata + +///////////// + +func (e InvalidArgsError) Error() string { + return p.Sprintf(e.Format, e.Args...) +} + +// InvalidArgs creates a InvalidArgsError +func InvalidArgs(msg string, args ...interface{}) InvalidArgsError { + return InvalidArgsError{Format: msg, Args: args} +} +func (e NotFoundError) Error() string { + return p.Sprintf(e.Format, e.Args...) +} + +// NotFound creates a NotFoundError +func NotFound(msg string, args ...interface{}) NotFoundError { + return NotFoundError{Format: msg, Args: args} +} + +func (e AlreadyExistsError) Error() string { + return p.Sprintf(e.Format, e.Args...) +} + +// AlreadyExists creates a AlreadyExistsError +func AlreadyExists(msg string, args ...interface{}) AlreadyExistsError { + return AlreadyExistsError{Format: msg, Args: args} +} + +func (e NotSupportedError) Error() string { + return p.Sprintf(e.Format, e.Args...) +} + +// NotSupported creates a NotSupportedError +func NotSupported(msg string, args ...interface{}) NotSupportedError { + return NotSupportedError{Format: msg, Args: args} +} + +func (e InternalError) Error() string { + return p.Sprintf(e.Format, e.Args...) +} + +// New creates an InternalError +func New(msg string, args ...interface{}) InternalError { + return InternalError{Format: msg, Args: args} +} diff --git a/src/translib/tlerr/tlerr.go b/src/translib/tlerr/tlerr.go new file mode 100644 index 0000000000..8ff1254301 --- /dev/null +++ b/src/translib/tlerr/tlerr.go @@ -0,0 +1,103 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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. + +The Error() method of the error interface for these functions +returns the English version, and are only meant for log files. + +For message strings that are returned to the users, the localization +will happen at when the GNMI/REST client's locale is known. +Hence, it cannot occur here. + +*/ +package tlerr + +import ( + // "fmt" + "cvl" + "golang.org/x/text/language" + "golang.org/x/text/message" + // "errors" + // "strings" +) + +var p *message.Printer + +func init() { + p = message.NewPrinter(language.English) +} + +// DB errors + +type TranslibDBCannotOpen struct { +} + +func (e TranslibDBCannotOpen) Error() string { + return p.Sprintf("Translib Redis Error: Cannot open DB") +} + +type TranslibDBNotInit struct { +} + +func (e TranslibDBNotInit) Error() string { + return p.Sprintf("Translib Redis Error: DB Not Initialized") +} + +type TranslibRedisClientEntryNotExist struct { + Entry string +} + +func (e TranslibRedisClientEntryNotExist) Error() string { + return p.Sprintf("Translib Redis Error: Entry does not exist: %s", e.Entry) +} + +type TranslibCVLFailure struct { + Code int + CVLErrorInfo cvl.CVLErrorInfo +} + +func (e TranslibCVLFailure) Error() string { + return p.Sprintf("Translib Redis Error: CVL Failure: %d: %v", e.Code, + e.CVLErrorInfo) +} + +type TranslibTransactionFail struct { +} + +func (e TranslibTransactionFail) Error() string { + return p.Sprintf("Translib Redis Error: Transaction Fails") +} + +type TranslibDBSubscribeFail struct { +} + +func (e TranslibDBSubscribeFail) Error() string { + return p.Sprintf("Translib Redis Error: DB Subscribe Fail") +} + +type TranslibSyntaxValidationError struct { + StatusCode int // status code + ErrorStr error // error message +} + +func (e TranslibSyntaxValidationError) Error() string { + return p.Sprintf("%s", e.ErrorStr) +} diff --git a/src/translib/transformer/host_comm.go b/src/translib/transformer/host_comm.go new file mode 100644 index 0000000000..0f625e8e07 --- /dev/null +++ b/src/translib/transformer/host_comm.go @@ -0,0 +1,122 @@ +package transformer + +import ( + // Go 1.11 doesn't let us download using the canonical import path + // "github.com/godbus/dbus/v5" + // This works around that problem by using gopkg.in instead + "gopkg.in/godbus/dbus.v5" +) + +// hostResult contains the body of the response and the error if any, when the +// endpoint finishes servicing the D-Bus request. +type hostResult struct { + Body []interface{} + Err error +} + +// hostQuery calls the corresponding D-Bus endpoint on the host and returns +// any error and response body +func hostQuery(endpoint string, args ...interface{}) (result hostResult) { + result_ch, err := hostQueryAsync(endpoint, args...) + + if err != nil { + result.Err = err + return + } + + result = <-result_ch + return +} + +// hostQueryAsync calls the corresponding D-Bus endpoint on the host and returns +// a channel for the result, and any error +func hostQueryAsync(endpoint string, args ...interface{}) (chan hostResult, error) { + var result_ch = make(chan hostResult, 1) + conn, err := dbus.SystemBus() + if err != nil { + return result_ch, err + } + + const bus_name = "org.SONiC.HostService" + const bus_path = "/org/SONiC/HostService" + + obj := conn.Object(bus_name, bus_path) + dest := bus_name + "." + endpoint + dbus_ch := make(chan *dbus.Call, 1) + + go func() { + var result hostResult + + // Wait for a read on the channel + call := <-dbus_ch + + if call.Err != nil { + result.Err = call.Err + } else { + result.Body = call.Body + } + + // Write the result to the channel + result_ch <- result + }() + + call := obj.Go(dest, 0, dbus_ch, args...) + + if call.Err != nil { + return result_ch, call.Err + } + + return result_ch, nil +} + +// Example +/* +ztpAction calls the ZTP endpoint on the host and returns the status +func ztpAction(action string) (string, error) { + var output string + // result.Body is of type []interface{}, since any data may be returned by + // the host server. The application is responsible for performing + // type assertions to get the correct data. + result := hostQuery("ztp." + action) + + if result.Err != nil { + return output, result.Err + } + + if action == "status" { + // ztp.status returns an exit code and the stdout of the command + // We only care about the stdout (which is at [1] in the slice) + output, _ = result.Body[1].(string) + } + + return output, nil +} + +// The following uses the hostQueryAsync option +func ztpAction(action string) (string, error) { + var output string + // body is of type []interface{}, since any data may be returned by + // the host server. The application is responsible for performing + // type assertions to get the correct data. + ch, err := hostQueryAsync("ztp." + action) + + if err != nil { + return output, err + } + + // Wait for the call to finish + result := <-ch + if result.Err != nil { + return output, result.Err + } + + if action == "status" { + // ztp.status returns an exit code and the stdout of the command + // We only care about the stdout (which is at [1] in the slice) + output, _ = result.Body[1].(string) + } + + return output, nil +} + +*/ diff --git a/src/translib/transformer/transformer.go b/src/translib/transformer/transformer.go new file mode 100644 index 0000000000..91e00c8c46 --- /dev/null +++ b/src/translib/transformer/transformer.go @@ -0,0 +1,173 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" +) + +var 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() { + initYangModelsPath() + 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 initYangModelsPath() { + if path, ok := os.LookupEnv("YANG_MODELS_PATH"); ok { + if !strings.HasSuffix(path, "/") { + path += "/" + } + YangPath = path + } + + fmt.Println("Yang modles path:", YangPath) +} + +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..052c4ac4ca --- /dev/null +++ b/src/translib/transformer/xconst.go @@ -0,0 +1,52 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 = "leaf-list" + + 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 + SONIC_MDL_PFX = "sonic" + +) diff --git a/src/translib/transformer/xfmr_acl.go b/src/translib/transformer/xfmr_acl.go new file mode 100644 index 0000000000..46e6e8bb93 --- /dev/null +++ b/src/translib/transformer/xfmr_acl.go @@ -0,0 +1,1001 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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("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_set_key_xfmr", YangToDb_acl_set_key_xfmr) + XlateFuncBind("DbToYang_acl_set_key_xfmr", DbToYang_acl_set_key_xfmr) + XlateFuncBind("YangToDb_acl_entry_key_xfmr", YangToDb_acl_entry_key_xfmr) + XlateFuncBind("DbToYang_acl_entry_key_xfmr", DbToYang_acl_entry_key_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" + DEFAULT_RULE = "DEFAULT_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" + ACTION_DROP = "DROP" + IP_TYPE_ANY = "ANY" + 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") + retDbDataMap := (*inParams.dbDataMap)[inParams.curDb] + //Check if a default ACL Rule exists, else create one and update the resultMap with default rule + for tblName, tblData := range retDbDataMap { + if tblName == ACL_TABLE { + ruleTs := &db.TableSpec{Name: RULE_TABLE} + for aclName, dbFldValData := range tblData { + keyStr := aclName + "|" + DEFAULT_RULE + existingEntry, err := inParams.d.GetEntry(ruleTs, db.Key{Comp: []string{keyStr}}) + // If Default Rule already exists, Do not add new Default Rule + if existingEntry.IsPopulated() && err == nil { + log.Info("DEFAULT_RULE exists", dbFldValData) + } else { + // Create default rule entry + aclRule := retDbDataMap[RULE_TABLE] + val := make(map[string]string) + aclRule[keyStr] = db.Value{Field: val} + aclRule[keyStr].Field["PRIORITY"] = strconv.FormatInt(int64(MIN_PRIORITY), 10) + aclRule[keyStr].Field["PACKET_ACTION"] = ACTION_DROP + aclRule[keyStr].Field["IP_TYPE"] = IP_TYPE_ANY + } + } + } + } + return retDbDataMap, 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_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var aclkey string + var err error + var oc_aclType ocbinds.E_OpenconfigAcl_ACL_TYPE + log.Info("YangToDb_acl_set_key_xfmr: ", inParams.ygRoot, inParams.uri) + pathInfo := NewPathInfo(inParams.uri) + + if len(pathInfo.Vars) < 2 { + err = errors.New("Invalid xpath, key attributes not found") + return aclkey, err + } + + oc_aclType, err = getAclTypeOCEnumFromName(pathInfo.Var("type")) + if err != nil { + err = errors.New("OC Acl type name to OC Acl Enum failed") + return aclkey, err + } + + aclkey = getAclKeyStrFromOCKey(pathInfo.Var("name"), oc_aclType) + log.Info("YangToDb_acl_set_key_xfmr - acl_set_key : ", aclkey) + + return aclkey, err +} + +var DbToYang_acl_set_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + var aclNameStr string + var aclTypeStr string + aclkey := inParams.key + log.Info("DbToYang_acl_set_key_xfmr: ", aclkey) + if strings.Contains(aclkey, "_"+OPENCONFIG_ACL_TYPE_IPV4) { + aclNameStr = strings.Replace(aclkey, "_"+OPENCONFIG_ACL_TYPE_IPV4, "", 1) + aclTypeStr = "ACL_IPV4" + } else if strings.Contains(aclkey, "_"+OPENCONFIG_ACL_TYPE_IPV6) { + aclNameStr = strings.Replace(aclkey, "_"+OPENCONFIG_ACL_TYPE_IPV6, "", 1) + aclTypeStr = "ACL_IPV6" + } else if strings.Contains(aclkey, "_"+OPENCONFIG_ACL_TYPE_L2) { + aclNameStr = strings.Replace(aclkey, "_"+OPENCONFIG_ACL_TYPE_L2, "", 1) + aclTypeStr = "ACL_L2" + } else { + err = errors.New("Invalid key for acl set.") + log.Info("Invalid Keys for acl acl set", aclkey) + } + rmap["name"] = aclNameStr + rmap["type"] = aclTypeStr + return rmap, 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 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 + } + 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 + + // repopulate 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_fdb.go b/src/translib/transformer/xfmr_fdb.go new file mode 100644 index 0000000000..8e511941f3 --- /dev/null +++ b/src/translib/transformer/xfmr_fdb.go @@ -0,0 +1,96 @@ +package transformer + +import ( + "errors" + "strings" + "strconv" + "translib/ocbinds" +) + +func init () { + XlateFuncBind("YangToDb_fdb_tbl_key_xfmr", YangToDb_fdb_tbl_key_xfmr) + XlateFuncBind("DbToYang_fdb_tbl_key_xfmr", DbToYang_fdb_tbl_key_xfmr) + XlateFuncBind("YangToDb_entry_type_field_xfmr", YangToDb_entry_type_field_xfmr) + XlateFuncBind("DbToYang_entry_type_field_xfmr", DbToYang_entry_type_field_xfmr) +} + +const ( + FDB_TABLE = "FDB_TABLE" + SONIC_ENTRY_TYPE_STATIC = "static" + SONIC_ENTRY_TYPE_DYNAMIC = "dynamic" + ENTRY_TYPE = "entry-type" +) + +/* E_OpenconfigNetworkInstance_ENTRY_TYPE */ +var FDB_ENTRY_TYPE_MAP = map[string]string{ + strconv.FormatInt(int64(ocbinds.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Fdb_MacTable_Entries_Entry_State_EntryType_STATIC), 10): SONIC_ENTRY_TYPE_STATIC, + strconv.FormatInt(int64(ocbinds.OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Fdb_MacTable_Entries_Entry_State_EntryType_DYNAMIC), 10): SONIC_ENTRY_TYPE_DYNAMIC, +} + + +var YangToDb_fdb_tbl_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var entry_key string + var err error + + pathInfo := NewPathInfo(inParams.uri) + vlanName := pathInfo.Var("vlan") + macAddress := pathInfo.Var("mac-address") + + vlanString := strings.Contains(vlanName,"Vlan") + if vlanString == false { + vlanName = "Vlan"+vlanName + } + entry_key = vlanName + ":" + macAddress + return entry_key, err +} + +var DbToYang_fdb_tbl_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + entry_key := inParams.key + + keyMap := strings.SplitN(entry_key, ":",2) + if len(keyMap) < 2 { + err = errors.New("Invalid key for INTERFACE table entry.") + return rmap, err + } + + vlanName := keyMap[0] + macAddress := keyMap[1] + vlanNumber := strings.SplitN(vlanName, "Vlan",2) + vlanId, err := strconv.ParseFloat(vlanNumber[1],64) + + rmap["vlan"] = vlanId + rmap["mac-address"] = macAddress + + return rmap, err +} + +var YangToDb_entry_type_field_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + + return res_map, err +} + +var DbToYang_entry_type_field_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + data := (*inParams.dbDataMap)[inParams.curDb] + fdbTableMap := data["FDB_TABLE"] + var entryTypeFinal = "" + if val, keyExist := fdbTableMap[inParams.key]; keyExist { + if entryType, ok := val.Field["type"]; ok { + entryTypeFinal = entryType + } else { + return result, err + } + } else { + return result, err + } + oc_entrytype := findInMap(FDB_ENTRY_TYPE_MAP, entryTypeFinal) + n, err := strconv.ParseInt(oc_entrytype, 10, 64) + result[ENTRY_TYPE] = ocbinds.E_OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Fdb_MacTable_Entries_Entry_State_EntryType(n).ΛMap()["E_OpenconfigNetworkInstance_NetworkInstances_NetworkInstance_Fdb_MacTable_Entries_Entry_State_EntryType"][n].Name + + return result, err +} diff --git a/src/translib/transformer/xfmr_interface.go b/src/translib/transformer/xfmr_interface.go new file mode 100644 index 0000000000..14255f43aa --- /dev/null +++ b/src/translib/transformer/xfmr_interface.go @@ -0,0 +1,141 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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{} + txCache 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) +/** + * RpcCallpoint is used to invoke a callback for action + * Param : []byte input payload, dbi indices + * Return : []byte output payload, error + **/ +type RpcCallpoint func (body []byte, dbs [db.MaxDB]*db.DB) ([]byte, error) +/** + * 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/xfmr_ptp.go b/src/translib/transformer/xfmr_ptp.go new file mode 100644 index 0000000000..4773f64c4b --- /dev/null +++ b/src/translib/transformer/xfmr_ptp.go @@ -0,0 +1,483 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 ( + log "github.com/golang/glog" + "errors" + "strings" + "strconv" + "github.com/openconfig/ygot/ygot" + "translib/ocbinds" + "fmt" + "path/filepath" + b64 "encoding/base64" +) + +func init() { + XlateFuncBind("YangToDb_ptp_entry_key_xfmr", YangToDb_ptp_entry_key_xfmr) + XlateFuncBind("DbToYang_ptp_entry_key_xfmr", DbToYang_ptp_entry_key_xfmr) + XlateFuncBind("YangToDb_ptp_port_entry_key_xfmr", YangToDb_ptp_port_entry_key_xfmr) + XlateFuncBind("DbToYang_ptp_port_entry_key_xfmr", DbToYang_ptp_port_entry_key_xfmr) + XlateFuncBind("YangToDb_ptp_global_key_xfmr", YangToDb_ptp_global_key_xfmr) + XlateFuncBind("DbToYang_ptp_global_key_xfmr", DbToYang_ptp_global_key_xfmr) + + XlateFuncBind("YangToDb_ptp_tcport_entry_key_xfmr", YangToDb_ptp_tcport_entry_key_xfmr) + XlateFuncBind("DbToYang_ptp_tcport_entry_key_xfmr", DbToYang_ptp_tcport_entry_key_xfmr) + XlateFuncBind("YangToDb_ptp_clock_identity_xfmr", YangToDb_ptp_clock_identity_xfmr) + XlateFuncBind("DbToYang_ptp_clock_identity_xfmr", DbToYang_ptp_clock_identity_xfmr) + XlateFuncBind("YangToDb_ptp_boolean_xfmr", YangToDb_ptp_boolean_xfmr) + XlateFuncBind("DbToYang_ptp_boolean_xfmr", DbToYang_ptp_boolean_xfmr) + XlateFuncBind("YangToDb_ptp_delay_mech_xfmr", YangToDb_ptp_delay_mech_xfmr) + XlateFuncBind("DbToYang_ptp_delay_mech_xfmr", DbToYang_ptp_delay_mech_xfmr) + XlateFuncBind("YangToDb_ptp_port_state_xfmr", YangToDb_ptp_port_state_xfmr) + XlateFuncBind("DbToYang_ptp_port_state_xfmr", DbToYang_ptp_port_state_xfmr) + XlateFuncBind("YangToDb_ptp_inst_number_xfmr:", YangToDb_ptp_inst_number_xfmr); + XlateFuncBind("DbToYang_ptp_inst_number_xfmr:", DbToYang_ptp_inst_number_xfmr); +} + +/* E_IETFPtp_DelayMechanismEnumeration */ +var PTP_DELAY_MECH_MAP = map[string]string{ + strconv.FormatInt(int64(ocbinds.IETFPtp_DelayMechanismEnumeration_e2e), 10): "E2E", + strconv.FormatInt(int64(ocbinds.IETFPtp_DelayMechanismEnumeration_p2p), 10): "P2P", + strconv.FormatInt(int64(ocbinds.IETFPtp_DelayMechanismEnumeration_UNSET), 10): "Auto", +} + +type ptp_id_bin [8]byte + +// ParseIdentity parses an s with the following format +// 010203.0405.060708 +func ParseIdentity(s string) (ptp_id ptp_id_bin, err error) { + if len(s) < 18 { + return ptp_id, fmt.Errorf("Invalid input identity string %s", s) + } + fmt.Sscanf(s, "%02x%02x%02x.%02x%02x.%02x%02x%02x", &ptp_id[0], &ptp_id[1], &ptp_id[2], &ptp_id[3], &ptp_id[4], &ptp_id[5], &ptp_id[6], &ptp_id[7]) + return ptp_id, err +} + +//////////////////////////////////////////// +// Bi-directoonal overloaded methods +//////////////////////////////////////////// +var YangToDb_ptp_entry_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var entry_key string + var err error + log.Info("YangToDb_ptp_entry_key_xfmr: ", inParams.ygRoot, " XPath ", inParams.uri, " key: ", inParams.key) + pathInfo := NewPathInfo(inParams.uri) + log.Info("YangToDb_ptp_entry_key_xfmr len(pathInfo.Vars): ", len(pathInfo.Vars)) + if len(pathInfo.Vars) < 1 { + err = errors.New("Invalid xpath, key attributes not found") + return entry_key, err + } + + inkey,_ := strconv.ParseUint(pathInfo.Var("instance-number"), 10, 64) + if inkey > 0 { + err = errors.New("Invalid input instance-number") + return entry_key, err + } + + entry_key = "GLOBAL" + + log.Info("YangToDb_ptp_entry_key_xfmr - entry_key : ", entry_key) + + return entry_key, err +} + +var DbToYang_ptp_entry_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + log.Info("DbToYang_ptp_entry_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + // rmap["instance-number"] = 0 + return rmap, err +} + +var YangToDb_ptp_port_entry_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var entry_key string + var err error + log.Info("YangToDb_ptp_port_entry_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + pathInfo := NewPathInfo(inParams.uri) + + log.Info("YangToDb_ptp_port_entry_key_xfmr len(pathInfo.Vars): ", len(pathInfo.Vars)) + if len(pathInfo.Vars) < 2 { + err = errors.New("Invalid xpath, key attributes not found") + return entry_key, err + } + + log.Info("YangToDb_ptp_port_entry_key_xfmr pathInfo.Var:port-number: ", pathInfo.Var("port-number")) + entry_key = "GLOBAL|Ethernet" + pathInfo.Var("port-number") + + + log.Info("YangToDb_ptp_port_entry_key_xfmr - entry_key : ", entry_key) + + return entry_key, err +} + +var DbToYang_ptp_port_entry_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + + log.Info("DbToYang_ptp_port_entry_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + entry_key := inParams.key + log.Info("DbToYang_ptp_port_entry_key_xfmr: ", entry_key) + + portName := entry_key + port_num := strings.Replace(portName, "GLOBAL|Ethernet", "", 1) + // rmap["instance-number"] = 0 + rmap["port-number"], _ = strconv.ParseInt(port_num, 10, 16) + log.Info("DbToYang_ptp_port_entry_key_xfmr port-number: ", port_num) + return rmap, err +} + +var YangToDb_ptp_global_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var entry_key string + var err error + log.Info("YangToDb_ptp_global_key_xfmr: ", inParams.ygRoot, inParams.uri) + + entry_key = "GLOBAL" + + log.Info("YangToDb_ptp_global_key_xfmr - entry_key : ", entry_key) + + return entry_key, err +} + +var DbToYang_ptp_global_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + log.Info("DbToYang_ptp_global_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + rmap["instance-number"] = 0 + return rmap, err +} + +var YangToDb_ptp_tcport_entry_key_xfmr KeyXfmrYangToDb = func(inParams XfmrParams) (string, error) { + var entry_key string + var err error + log.Info("YangToDb_ptp_tcport_entry_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + pathInfo := NewPathInfo(inParams.uri) + + log.Info("YangToDb_ptp_tcport_entry_key_xfmr len(pathInfo.Vars): ", len(pathInfo.Vars)) + if len(pathInfo.Vars) < 1 { + err = errors.New("Invalid xpath, key attributes not found") + return entry_key, err + } + + log.Info("YangToDb_ptp_tcport_entry_key_xfmr pathInfo.Var:port-number: ", pathInfo.Var("port-number")) + entry_key = "Ethernet" + pathInfo.Var("port-number") + + + log.Info("YangToDb_ptp_tcport_entry_key_xfmr - entry_key : ", entry_key) + + return entry_key, err +} + +var DbToYang_ptp_tcport_entry_key_xfmr KeyXfmrDbToYang = func(inParams XfmrParams) (map[string]interface{}, error) { + rmap := make(map[string]interface{}) + var err error + log.Info("DbToYang_ptp_tcport_entry_key_xfmr root, uri: ", inParams.ygRoot, inParams.uri) + + entry_key := inParams.key + log.Info("DbToYang_ptp_tcport_entry_key_xfmr: ", entry_key) + + portName := entry_key + port_num := strings.Replace(portName, "Ethernet", "", 1) + rmap["port-number"], _ = strconv.ParseInt(port_num, 10, 16) + log.Info("DbToYang_ptp_tcport_entry_key_xfmr port-number: ", port_num) + return rmap, err +} + +func getPtpRoot(s *ygot.GoStruct) *ocbinds.IETFPtp_Ptp { + deviceObj := (*s).(*ocbinds.Device) + return deviceObj.Ptp +} + +var YangToDb_ptp_clock_identity_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + var field string + var identity []byte + if inParams.param == nil { + log.Info("YangToDb_ptp_clock_identity_xfmr Error: ") + return res_map, err + } + log.Info("YangToDb_ptp_clock_identity_xfmr : ", *inParams.ygRoot, " Xpath: ", inParams.uri) + log.Info("YangToDb_ptp_clock_identity_xfmr inParams.key: ", inParams.key) + + ptpObj := getPtpRoot(inParams.ygRoot) + + if strings.Contains(inParams.uri, "grandmaster-identity") { + identity = ptpObj.InstanceList[0].ParentDs.GrandmasterIdentity + field = "grandmaster-identity" + } else if strings.Contains(inParams.uri, "parent-port-identity") { + identity = ptpObj.InstanceList[0].ParentDs.ParentPortIdentity.ClockIdentity + field = "clock-identity" + } else if strings.Contains(inParams.uri, "transparent-clock-default-ds") { + identity = ptpObj.TransparentClockDefaultDs.ClockIdentity + field = "clock-identity" + } else if strings.Contains(inParams.uri, "default-ds") { + identity = ptpObj.InstanceList[0].DefaultDs.ClockIdentity + field = "clock-identity" + } + + + enc := fmt.Sprintf("%02x%02x%02x.%02x%02x.%02x%02x%02x", + identity[0], identity[1], identity[2], identity[3], identity[4], identity[5], identity[6], identity[7]) + + log.Info("YangToDb_ptp_clock_identity_xfmr enc: ", enc, " field: ", field) + res_map[field] = enc + return res_map, err +} + +var DbToYang_ptp_clock_identity_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + var ptp_id ptp_id_bin + var field,identity,sEnc string + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_ptp_clock_identity_xfmr ygRoot: ", *inParams.ygRoot, " Xpath: ", inParams.uri, " data: ", data) + + if strings.Contains(inParams.uri, "grandmaster-identity") { + field = "grandmaster-identity" + identity = data["PTP_PARENTDS"][inParams.key].Field[field] + } else if strings.Contains(inParams.uri, "parent-port-identity") { + field = "clock-identity" + identity = data["PTP_PARENTDS"][inParams.key].Field[field] + } else if strings.Contains(inParams.uri, "transparent-clock-default-ds") { + field = "clock-identity" + identity = data["PTP_TC_CLOCK"][inParams.key].Field[field] + } else if strings.Contains(inParams.uri, "default-ds") { + field = "clock-identity" + identity = data["PTP_CLOCK"][inParams.key].Field[field] + } + if len(identity) >= 18 { + ptp_id,err = ParseIdentity(identity) + sEnc = b64.StdEncoding.EncodeToString(ptp_id[:]) + result[field] = sEnc + } else { + sEnc = "" + } + + return result, err +} + +var YangToDb_ptp_boolean_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + var outval string + if inParams.param == nil { + log.Info("YangToDb_ptp_boolean_xfmr Error: ") + return res_map, err + } + log.Info("YangToDb_ptp_boolean_xfmr : ", *inParams.ygRoot, " Xpath: ", inParams.uri) + log.Info("YangToDb_ptp_boolean_xfmr inParams.key: ", inParams.key) + log.Info("YangToDb_ptp_boolean_xfmr inParams.curDb: ", inParams.curDb) + + inval, _ := inParams.param.(*bool) + _, field := filepath.Split(inParams.uri) + log.Info("YangToDb_ptp_boolean_xfmr inval: ", inval, " field: ", field) + + if (*inval) { + outval = "1" + } else { + outval = "0" + } + + log.Info("YangToDb_ptp_boolean_xfmr outval: ", outval, " field: ", field) + res_map[field] = outval + return res_map, err +} + +var DbToYang_ptp_boolean_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + var inval string + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_ptp_boolean_xfmr ygRoot: ", *inParams.ygRoot, " Xpath: ", inParams.uri, " data: ", data) + + _, field := filepath.Split(inParams.uri) + if field == "two-step-flag" { + inval = data["PTP_CLOCK"][inParams.key].Field[field] + } else if field == "slave-only" { + inval = data["PTP_CLOCK"][inParams.key].Field[field] + } else if field == "parent-stats" { + inval = data["PTP_PARENTDS"][inParams.key].Field[field] + } else if field == "current-utc-offset-valid" { + inval = data["TIMEPROPDS"][inParams.key].Field[field] + } else if field == "leap59" { + inval = data["TIMEPROPDS"][inParams.key].Field[field] + } else if field == "leap61" { + inval = data["TIMEPROPDS"][inParams.key].Field[field] + } else if field == "time-traceable" { + inval = data["TIMEPROPDS"][inParams.key].Field[field] + } else if field == "frequency-traceable" { + inval = data["TIMEPROPDS"][inParams.key].Field[field] + } else if field == "ptp-timescale" { + inval = data["TIMEPROPDS"][inParams.key].Field[field] + } else if field == "faulty-flag" { + inval = data["PTP_TC_PORT"][inParams.key].Field[field] + } + + if (inval == "0") { + result[field] = false + } else if inval == "1" { + result[field] = true + } + + return result, err +} + +var YangToDb_ptp_delay_mech_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + var outval string + if inParams.param == nil { + log.Info("YangToDb_ptp_delay_mech_xfmr Error: ") + return res_map, err + } + log.Info("YangToDb_ptp_delay_mech_xfmr : ", *inParams.ygRoot, " Xpath: ", inParams.uri) + log.Info("YangToDb_ptp_delay_mech_xfmr inParams.key: ", inParams.key) + + inval, _ := inParams.param.(ocbinds.E_IETFPtp_DelayMechanismEnumeration) + _, field := filepath.Split(inParams.uri) + + log.Info("YangToDb_ptp_delay_mech_xfmr outval: ", outval, " field: ", field) + res_map[field] = findInMap(PTP_DELAY_MECH_MAP, strconv.FormatInt(int64(inval), 10)) + + return res_map, err +} + +var DbToYang_ptp_delay_mech_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + result := make(map[string]interface{}) + var inval string + var outval string + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_ptp_delay_mech_xfmr ygRoot: ", *inParams.ygRoot, " Xpath: ", inParams.uri, " data: ", data) + + _, field := filepath.Split(inParams.uri) + + if strings.Contains(inParams.uri, "port-ds-list") { + inval = data["PTP_PORT"][inParams.key].Field[field] + } else if strings.Contains(inParams.uri, "transparent-clock-default-ds") { + inval = data["PTP_TC_CLOCK"][inParams.key].Field[field] + } + + switch inval { + case "E2E": + outval = "e2e" + case "P2P": + outval = "p2p" + default: + outval = "" + } + log.Info("DbToYang_ptp_delay_mech_xfmr result: ", outval, " inval: ", inval) + if outval != "" { + result[field] = outval + } + + return result, err +} + +var YangToDb_ptp_port_state_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + var err error + var outval string + if inParams.param == nil { + log.Info("YangToDb_ptp_port_state_xfmr Error: ") + return res_map, err + } + log.Info("YangToDb_ptp_port_state_xfmr : ", *inParams.ygRoot, " Xpath: ", inParams.uri) + log.Info("YangToDb_ptp_port_state_xfmr inParams.key: ", inParams.key) + + inval, _ := inParams.param.(ocbinds.E_IETFPtp_PortStateEnumeration) + _, field := filepath.Split(inParams.uri) + + switch inval { + case ocbinds.IETFPtp_PortStateEnumeration_initializing: + outval = "1" + case ocbinds.IETFPtp_PortStateEnumeration_faulty: + outval = "2" + case ocbinds.IETFPtp_PortStateEnumeration_disabled: + outval = "3" + case ocbinds.IETFPtp_PortStateEnumeration_listening: + outval = "4" + case ocbinds.IETFPtp_PortStateEnumeration_pre_master: + outval = "5" + case ocbinds.IETFPtp_PortStateEnumeration_master: + outval = "6" + case ocbinds.IETFPtp_PortStateEnumeration_passive: + outval = "7" + case ocbinds.IETFPtp_PortStateEnumeration_uncalibrated: + outval = "8" + case ocbinds.IETFPtp_PortStateEnumeration_slave: + outval = "9" + } + + log.Info("YangToDb_ptp_port_state_xfmr outval: ", outval, " field: ", field) + res_map[field] = outval + return res_map, err +} + +var DbToYang_ptp_port_state_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + var err error + var inval string + var outval string + result := make(map[string]interface{}) + + data := (*inParams.dbDataMap)[inParams.curDb] + log.Info("DbToYang_ptp_port_state_xfmr :", data, inParams.ygRoot) + + inval = data["PTP_PORT"][inParams.key].Field["port-state"] + switch inval { + case "1": + outval = "initializing" + case "2": + outval = "faulty" + case "3": + outval = "disabled" + case "4": + outval = "listening" + case "5": + outval = "pre-master" + case "6": + outval = "master" + case "7": + outval = "passive" + case "8": + outval = "uncalibrated" + case "9": + outval = "slave" + default: + goto done + } + result["port-state"] = outval +done: + return result, err +} + +var YangToDb_ptp_inst_number_xfmr FieldXfmrYangToDb = func(inParams XfmrParams) (map[string]string, error) { + res_map := make(map[string]string) + res_map["NULL"] = "NULL" + return res_map, nil +} + + +var DbToYang_ptp_inst_number_xfmr FieldXfmrDbtoYang = func(inParams XfmrParams) (map[string]interface{}, error) { + /* do nothing */ + var err error + result := make(map[string]interface{}) + return result, err +} diff --git a/src/translib/transformer/xfmr_sonic_tests.go b/src/translib/transformer/xfmr_sonic_tests.go new file mode 100644 index 0000000000..ec019d03e0 --- /dev/null +++ b/src/translib/transformer/xfmr_sonic_tests.go @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "encoding/json" + "translib/tlerr" + "translib/db" + "github.com/golang/glog" +) + +func init() { + XlateFuncBind("rpc_sum_cb", rpc_sum_cb) +} + +var rpc_sum_cb RpcCallpoint = func(body []byte, dbs [db.MaxDB]*db.DB) ([]byte, error) { + var err error + var operand struct { + Input struct { + Left int32 `json:"left"` + Right int32 `json:"right"` + } `json:"sonic-tests:input"` + } + + err = json.Unmarshal(body, &operand) + if err != nil { + glog.Errorf("Failed to parse rpc input; err=%v", err) + return nil,tlerr.InvalidArgs("Invalid rpc input") + } + + var sum struct { + Output struct { + Result int32 `json:"result"` + } `json:"sonic-tests:output"` + } + + sum.Output.Result = operand.Input.Left + operand.Input.Right + result, err := json.Marshal(&sum) + + return result, err +} diff --git a/src/translib/transformer/xfmr_system.go b/src/translib/transformer/xfmr_system.go new file mode 100644 index 0000000000..d7c7032fb7 --- /dev/null +++ b/src/translib/transformer/xfmr_system.go @@ -0,0 +1,308 @@ +package transformer + +import ( + "encoding/json" + "translib/ocbinds" + "translib/tlerr" + "time" + "io/ioutil" + "syscall" + "strconv" + "os" + log "github.com/golang/glog" + ygot "github.com/openconfig/ygot/ygot" +) + +func init () { + XlateFuncBind("DbToYang_sys_state_xfmr", DbToYang_sys_state_xfmr) + XlateFuncBind("DbToYang_sys_memory_xfmr", DbToYang_sys_memory_xfmr) + XlateFuncBind("DbToYang_sys_cpus_xfmr", DbToYang_sys_cpus_xfmr) + XlateFuncBind("DbToYang_sys_procs_xfmr", DbToYang_sys_procs_xfmr) +} + +type JSONSystem struct { + Hostname string `json:"hostname"` + Total uint64 `json:"total"` + Used uint64 `json:"used"` + Free uint64 `json:"free"` + + Cpus []Cpu `json:"cpus"` + Procs map[string]Proc `json:"procs"` + +} + +type Cpu struct { + User int64 `json:"user"` + System int64 `json:"system"` + Idle int64 `json:"idle"` +} + +type Proc struct { + Cmd string `json:"cmd"` + Start uint64 `json:"start"` + User uint64 `json:"user"` + System uint64 `json:"system"` + Mem uint64 `json:"mem"` + Cputil float32 `json:"cputil"` + Memutil float32 `json:"memutil"` +} + +type CpuState struct { + user uint8 + system uint8 + idle uint8 +} + +type ProcessState struct { + Args [] string + CpuUsageSystem uint64 + CpuUsageUser uint64 + CpuUtilization uint8 + MemoryUsage uint64 + MemoryUtilization uint8 + Name string + Pid uint64 + StartTime uint64 + Uptime uint64 +} + +func getAppRootObject(inParams XfmrParams) (*ocbinds.OpenconfigSystem_System) { + deviceObj := (*inParams.ygRoot).(*ocbinds.Device) + return deviceObj.System +} + +func getSystemInfoFromFile () (JSONSystem, error) { + log.Infof("getSystemInfoFromFile Enter") + + var jsonsystem JSONSystem + jsonFile, err := os.Open("/mnt/platform/system") + if err != nil { + log.Infof("system json open failed") + errStr := "Information not available or Platform support not added" + terr := tlerr.NotFoundError{Format: errStr} + return jsonsystem, terr + } + syscall.Flock(int(jsonFile.Fd()),syscall.LOCK_EX) + log.Infof("syscall.Flock done") + + defer jsonFile.Close() + defer log.Infof("jsonFile.Close called") + defer syscall.Flock(int(jsonFile.Fd()), syscall.LOCK_UN); + defer log.Infof("syscall.Flock unlock called") + + byteValue, _ := ioutil.ReadAll(jsonFile) + err = json.Unmarshal(byteValue, &jsonsystem) + if err != nil { + errStr := "json.Unmarshal failed" + terr := tlerr.InternalError{Format: errStr} + return jsonsystem, terr + + } + return jsonsystem, nil +} + +func getSystemState (sys *JSONSystem, sysstate *ocbinds.OpenconfigSystem_System_State) () { + log.Infof("getSystemState Entry") + + crtime := time.Now().Format(time.RFC3339) + "+00:00" + + sysstate.Hostname = &sys.Hostname + sysstate.CurrentDatetime = &crtime; + sysinfo := syscall.Sysinfo_t{} + + sys_err := syscall.Sysinfo(&sysinfo) + if sys_err == nil { + boot_time := uint64 (time.Now().Unix() - sysinfo.Uptime) + sysstate.BootTime = &boot_time + } +} + + +var DbToYang_sys_state_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var err error + + sysObj := getAppRootObject(inParams) + + jsonsystem, err := getSystemInfoFromFile() + if err != nil { + log.Infof("getSystemInfoFromFile failed") + return err + } + ygot.BuildEmptyTree(sysObj) + getSystemState(&jsonsystem, sysObj.State) + return err; +} + +func getSystemMemory (sys *JSONSystem, sysmem *ocbinds.OpenconfigSystem_System_Memory_State) () { + log.Infof("getSystemMemory Entry") + + sysmem.Physical = &sys.Total + sysmem.Reserved = &sys.Used +} + +var DbToYang_sys_memory_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var err error + + sysObj := getAppRootObject(inParams) + + jsonsystem, err := getSystemInfoFromFile() + if err != nil { + log.Infof("getSystemInfoFromFile failed") + return err + } + ygot.BuildEmptyTree(sysObj) + + sysObj.Memory.State = &ocbinds.OpenconfigSystem_System_Memory_State{} + getSystemMemory(&jsonsystem, sysObj.Memory.State) + return err; +} + +func getSystemCpu (idx int, cpu Cpu, syscpus *ocbinds.OpenconfigSystem_System_Cpus) { + log.Infof("getSystemCpu Entry idx ", idx) + + sysinfo := syscall.Sysinfo_t{} + sys_err := syscall.Sysinfo(&sysinfo) + if sys_err != nil { + log.Infof("syscall.Sysinfo failed.") + } + + var index ocbinds.OpenconfigSystem_System_Cpus_Cpu_State_Index_Union_Uint32 + index.Uint32 = uint32(idx) + syscpu, err := syscpus.NewCpu(&index) + if err != nil { + log.Infof("syscpus.NewCpu failed") + return + } + ygot.BuildEmptyTree(syscpu) + syscpu.Index = &index + var cpucur CpuState + if idx == 0 { + cpucur.user = uint8((cpu.User/4)/sysinfo.Uptime) + cpucur.system = uint8((cpu.System/4)/sysinfo.Uptime) + cpucur.idle = uint8((cpu.Idle/4)/sysinfo.Uptime) + } else { + cpucur.user = uint8(cpu.User/sysinfo.Uptime) + cpucur.system = uint8(cpu.System/sysinfo.Uptime) + cpucur.idle = uint8(cpu.Idle/sysinfo.Uptime) + } + syscpu.State.User.Instant = &cpucur.user + syscpu.State.Kernel.Instant = &cpucur.system + syscpu.State.Idle.Instant = &cpucur.idle +} + +func getSystemCpus (sys *JSONSystem, syscpus *ocbinds.OpenconfigSystem_System_Cpus) { + log.Infof("getSystemCpus Entry") + + sysinfo := syscall.Sysinfo_t{} + sys_err := syscall.Sysinfo(&sysinfo) + if sys_err != nil { + log.Infof("syscall.Sysinfo failed.") + } + + for idx, cpu := range sys.Cpus { + getSystemCpu(idx, cpu, syscpus) + } +} + +var DbToYang_sys_cpus_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var err error + + sysObj := getAppRootObject(inParams) + + jsonsystem, err := getSystemInfoFromFile() + if err != nil { + log.Infof("getSystemInfoFromFile failed") + return err + } + + ygot.BuildEmptyTree(sysObj) + + path := NewPathInfo(inParams.uri) + val := path.Vars["index"] + if len(val) != 0 { + cpu, _ := strconv.Atoi(val) + log.Info("Cpu id: ", cpu, ", max is ", len(jsonsystem.Cpus)) + if cpu >=0 && cpu < len(jsonsystem.Cpus) { + getSystemCpu(cpu, jsonsystem.Cpus[cpu], sysObj.Cpus) + } else { + log.Info("Cpu id: ", cpu, "is invalid, max is ", len(jsonsystem.Cpus)) + } + } else { + getSystemCpus(&jsonsystem, sysObj.Cpus) + } + return err; +} + +func getSystemProcess (proc *Proc, sysproc *ocbinds.OpenconfigSystem_System_Processes_Process, pid uint64) { + + var procstate ProcessState + + ygot.BuildEmptyTree(sysproc) + procstate.CpuUsageUser = proc.User + procstate.CpuUsageSystem = proc.System + procstate.MemoryUsage = proc.Mem * 1024 + procstate.MemoryUtilization = uint8(proc.Memutil) + procstate.CpuUtilization = uint8(proc.Cputil) + procstate.Name = proc.Cmd + procstate.Pid = pid + procstate.StartTime = proc.Start * 1000000000 // ns + procstate.Uptime = uint64(time.Now().Unix()) - proc.Start + + sysproc.Pid = &procstate.Pid + sysproc.State.CpuUsageSystem = &procstate.CpuUsageSystem + sysproc.State.CpuUsageUser = &procstate.CpuUsageUser + sysproc.State.CpuUtilization = &procstate.CpuUtilization + sysproc.State.MemoryUsage = &procstate.MemoryUsage + sysproc.State.MemoryUtilization = &procstate.MemoryUtilization + sysproc.State.Name = &procstate.Name + sysproc.State.Pid = &procstate.Pid + sysproc.State.StartTime = &procstate.StartTime + sysproc.State.Uptime = &procstate.Uptime +} + +func getSystemProcesses (sys *JSONSystem, sysprocs *ocbinds.OpenconfigSystem_System_Processes, pid uint64) { + log.Infof("getSystemProcesses Entry") + + if pid != 0 { + proc := sys.Procs[strconv.Itoa(int(pid))] + sysproc := sysprocs.Process[pid] + + getSystemProcess(&proc, sysproc, pid) + } else { + + for pidstr, proc := range sys.Procs { + idx, _:= strconv.Atoi(pidstr) + + sysproc, err := sysprocs.NewProcess(uint64 (idx)) + if err != nil { + log.Infof("sysprocs.NewProcess failed") + return + } + + getSystemProcess(&proc, sysproc, uint64 (idx)) + } + } + return +} +var DbToYang_sys_procs_xfmr SubTreeXfmrDbToYang = func(inParams XfmrParams) error { + var err error + + sysObj := getAppRootObject(inParams) + + jsonsystem, err := getSystemInfoFromFile() + if err != nil { + log.Infof("getSystemInfoFromFile failed") + return err + } + + ygot.BuildEmptyTree(sysObj) + path := NewPathInfo(inParams.uri) + val := path.Vars["pid"] + pid := 0 + if len(val) != 0 { + pid, _ = strconv.Atoi(val) + } + getSystemProcesses(&jsonsystem, sysObj.Processes, uint64(pid)) + return err; +} + diff --git a/src/translib/transformer/xfmr_udld.go b/src/translib/transformer/xfmr_udld.go new file mode 100644 index 0000000000..e486f76b77 --- /dev/null +++ b/src/translib/transformer/xfmr_udld.go @@ -0,0 +1,14 @@ +package transformer + +import ( + log "github.com/golang/glog" +) + +func init() { + XlateFuncBind("YangToDb_udld_global_key_xfmr", YangToDb_udld_global_key_xfmr) +} + +var YangToDb_udld_global_key_xfmr = func(inParams XfmrParams) (string, error) { + log.Info("YangToDb_udld_global_key_xfmr: ", inParams.ygRoot, inParams.uri) + return "GLOBAL", nil +} diff --git a/src/translib/transformer/xlate.go b/src/translib/transformer/xlate.go new file mode 100644 index 0000000000..2583b1dbc3 --- /dev/null +++ b/src/translib/transformer/xlate.go @@ -0,0 +1,495 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "translib/tlerr" +) + +const ( + GET = 1 + iota + CREATE + REPLACE + UPDATE + DELETE +) + +type KeySpec struct { + dbNum db.DBNum + Ts db.TableSpec + Key db.Key + Child []KeySpec + ignoreParentKey bool +} + +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 support with regex patten, 'abc*' for optimization + keys, err := dbs[spec.dbNum].GetKeys(&spec.Ts) + if err != nil { + return err + } + log.Infof("keys for table %v in Db %v are %v", spec.Ts.Name, spec.dbNum, keys) + for i, _ := range keys { + if parentKey != nil && (spec.ignoreParentKey == false) { + // 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{}, txCache 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, txCache) + 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 len(xYangSpecMap[yangXpath].xfmrKey) > 0 || xYangSpecMap[yangXpath].keyName != nil { + dbFormat.ignoreParentKey = true + } else { + dbFormat.ignoreParentKey = false + } + 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{}, txCache 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 + } + + // Map contains 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, txCache) + 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, txCache) + 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, txCache) + 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, txCache) + 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, txCache interface{}) ([]byte, error) { + var err error + var payload []byte + log.Info("received xpath =", uri) + + keySpec, err := XlateUriToKeySpec(uri, ygRoot, nil, txCache) + 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, txCache) + 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, txCache interface{}) ([]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, txCache) + 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 +} + +func GetOrdTblList(xfmrTbl string, uriModuleNm string) []string { + var ordTblList []string + processedTbl := false + var sncMdlList []string + + sncMdlList = getYangMdlToSonicMdlList(uriModuleNm) + for _, sonicMdlNm := range(sncMdlList) { + sonicMdlTblInfo := xDbSpecTblSeqnMap[sonicMdlNm] + for _, ordTblNm := range(sonicMdlTblInfo.OrdTbl) { + if xfmrTbl == ordTblNm { + log.Infof("Found sonic module(%v) whose ordered table list contains table %v", sonicMdlNm, xfmrTbl) + ordTblList = sonicMdlTblInfo.OrdTbl + processedTbl = true + break + } + } + if processedTbl { + break + } + } + return ordTblList +} + +func GetTablesToWatch(xfmrTblList []string, uriModuleNm string) []string { + var depTblList []string + depTblMap := make(map[string]bool) //create to avoid duplicates in depTblList, serves as a Set + processedTbl := false + var sncMdlList []string + + sncMdlList = getYangMdlToSonicMdlList(uriModuleNm) + + for _, xfmrTbl := range(xfmrTblList) { + //can be optimized if there is a way to know all sonic modules, a given OC-Yang spans over + for _, sonicMdlNm := range(sncMdlList) { + sonicMdlTblInfo := xDbSpecTblSeqnMap[sonicMdlNm] + for _, ordTblNm := range(sonicMdlTblInfo.OrdTbl) { + if xfmrTbl == ordTblNm { + log.Infof("Found sonic module(%v) whose ordered table list contains table %v", sonicMdlNm, xfmrTbl) + ldepTblList := sonicMdlTblInfo.DepTbl[xfmrTbl] + for _, depTblNm := range(ldepTblList) { + depTblMap[depTblNm] = true + } + //assumption that a table belongs to only one sonic module + processedTbl = true + break + } + } + if processedTbl { + break + } + } + } + for depTbl := range(depTblMap) { + depTblList = append(depTblList, depTbl) + } + return depTblList +} + +func CallRpcMethod(path string, body []byte, dbs [db.MaxDB]*db.DB) ([]byte, error) { + var err error + var ret []byte + + // TODO - check module name + rpcName := strings.Split(path, ":") + if dbXpathData, ok := xDbSpecMap[rpcName[1]]; ok { + log.Info("RPC callback invoked (%v) \r\n", rpcName) + data, err := XlateFuncCall(dbXpathData.rpcFunc, body, dbs) + if err != nil { + return nil, err + } + ret = data[0].Interface().([]byte) + } else { + log.Error("No tsupported RPC", path) + err = tlerr.NotSupported("Not supported RPC") + } + + return ret, 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..3899be6c75 --- /dev/null +++ b/src/translib/transformer/xlate_from_db.go @@ -0,0 +1,844 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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{}, 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 resPtr 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) + var resInt8 int8 = int8(res.(int64)) + resPtr = &resInt8 + case yang.Yint16: + res, err = DbValToInt(dbFldVal, INTBASE, 16, false) + var resInt16 int16 = int16(res.(int64)) + resPtr = &resInt16 + case yang.Yint32: + res, err = DbValToInt(dbFldVal, INTBASE, 32, false) + var resInt32 int32 = int32(res.(int64)) + resPtr = &resInt32 + case yang.Yuint8: + res, err = DbValToInt(dbFldVal, INTBASE, 8, true) + var resUint8 uint8 = uint8(res.(uint64)) + resPtr = &resUint8 + case yang.Yuint16: + res, err = DbValToInt(dbFldVal, INTBASE, 16, true) + var resUint16 uint16 = uint16(res.(uint64)) + resPtr = &resUint16 + case yang.Yuint32: + res, err = DbValToInt(dbFldVal, INTBASE, 32, true) + var resUint32 uint32 = uint32(res.(uint64)) + resPtr = &resUint32 + case yang.Ybool: + if res, err = strconv.ParseBool(dbFldVal); err != nil { + log.Warningf("Non Bool type for yang leaf-list item %v", dbFldVal) + } + var resBool bool = res.(bool) + resPtr = &resBool + case yang.Ybinary, yang.Ydecimal64, yang.Yenum, yang.Yidentityref, yang.Yint64, yang.Yuint64, yang.Ystring, yang.Yunion,yang.Yleafref: + // 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 + var resString string = res.(string) + resPtr = &resString + 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, resPtr, 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, txCache interface{}) error { + var tblList []string + + if tbl == "" && xYangSpecMap[xpath].xfmrTbl != nil { + xfmrTblFunc := *xYangSpecMap[xpath].xfmrTbl + if len(xfmrTblFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, tblKey, dbDataMap, nil, txCache) + tblList = xfmrTblHandlerFunc(xfmrTblFunc, inParams) + if len(tblList) != 0 { + for _, curTbl := range tblList { + dbDataFromTblXfmrGet(curTbl, inParams, dbDataMap) + } + } + } + } else if tbl != "" && xYangSpecMap[xpath].xfmrTbl == nil { + tblList = append(tblList, tbl) + } else if tbl != "" && xYangSpecMap[xpath].xfmrTbl != nil { + /*key instance level GET, table name and table key filled from xpathKeyExtract which internally calls table transformer*/ + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, tblKey, dbDataMap, nil, txCache) + dbDataFromTblXfmrGet(tbl, inParams, dbDataMap) + tblList = append(tblList, tbl) + + } else if tbl == "" && xYangSpecMap[xpath].xfmrTbl == nil { + // Handling for case: Parent list is not associated with a tableName but has children containers/lists having tableNames. + if tblKey != "" { + mapSlice, _ := yangListInstanceDataFill(dbs, ygRoot, uri, xpath, dbDataMap, resultMap, tbl, tblKey, cdb, validate, txCache) + if len(mapSlice) > 0 { + listInstanceGet := false + // Check if it is a list instance level Get + if ((strings.HasSuffix(uri, "]")) || (strings.HasSuffix(uri, "]/"))) { + listInstanceGet = true + for k, v := range mapSlice[0] { + resultMap[k] = v + } + } + if !listInstanceGet { + resultMap[xYangSpecMap[xpath].yangEntry.Name] = mapSlice + } + } + } + } + + 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, txCache) + if len(xYangSpecMap[xpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, curUri, GET, "", dbDataMap, nil, txCache) + 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, txCache) + if dbKey == keyFromCurUri || keyFromCurUri == "" { + 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, txCache) + mapSlice = append(mapSlice, curMap) + } + } + } + if len(mapSlice) > 0 { + listInstanceGet := false + /*Check if it is a list instance level Get*/ + if ((strings.HasSuffix(uri, "]")) || (strings.HasSuffix(uri, "]/"))) { + listInstanceGet = true + for k, v := range mapSlice[0] { + resultMap[k] = v + } + } + if !listInstanceGet { + resultMap[xYangSpecMap[xpath].yangEntry.Name] = mapSlice + } + } else { + log.Infof("Empty slice for (\"%v\").\r\n", uri) + } + } + }// end of tblList for + return nil +} + +func yangListInstanceDataFill(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, dbKey string, cdb db.DBNum, validate bool, txCache interface{}) ([]typeMapOfInterface, error) { + + var err error + var mapSlice []typeMapOfInterface + curMap := make(map[string]interface{}) + curKeyMap, curUri, _ := dbKeyToYangDataConvert(uri, xpath, dbKey, dbs[cdb].Opts.KeySeparator, txCache) + if len(xYangSpecMap[xpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, curUri, GET, "", dbDataMap, nil, txCache) + 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, txCache) + if dbKey == keyFromCurUri || keyFromCurUri == "" { + 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, txCache) + mapSlice = append(mapSlice, curMap) + } + } + return mapSlice, err +} + +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, txCache interface{}) (map[string]interface{}, error) { + log.Infof("Received xpath - %v, uri - %v, table - %v, table key - %v", xpath, uri, 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, txCache) + 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, txCache interface{}) 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, txCache) + // TODO - handle non CONFIG-DB + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, key, dbDataMap, nil, txCache) + 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, txCache) + if err != nil { + log.Infof("Failed to get data(\"%v\").", chldUri) + } + for lf, val := range fldValMap { + resultMap[lf] = val + } + } else if chldYangType == YANG_CONTAINER { + _, tblKey, tbl = xpathKeyExtract(dbs[cdb], ygRoot, GET, chldUri, txCache) + 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, tblKey, dbDataMap, nil, txCache) + 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, txCache) + 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, txCache) + if len(cmap) > 0 { + resultMap[cname] = cmap + } else { + log.Infof("Empty container(\"%v\").\r\n", chldUri) + } + } + } else if chldYangType == YANG_LIST { + _, tblKey, tbl = xpathKeyExtract(dbs[cdb], ygRoot, GET, chldUri, txCache) + cdb = xYangSpecMap[chldXpath].dbIndex + if len(xYangSpecMap[chldXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, chldUri, GET, "", dbDataMap, nil, txCache) + 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, tblKey, cdb, isValid, txCache) + } + } 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, txCache interface{}) (string, error) { + var err 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, txCache) + 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, txCache) + res := validateHandlerFunc(inParams) + if !res { + validateHandlerFlag = true + /* cannot immediately return from here 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, txCache) + 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, txCache) + if err != nil { + log.Infof("Empty terminal node (\"%v\").", uri) + } + resultMap = fldValMap + break + + } else if yangType == YANG_CONTAINER { + cmap := make(map[string]interface{}) + resultMap = cmap + if validateHandlerFlag || tableXfmrFlag { + break + } + if len(xYangSpecMap[reqXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, "", dbDataMap, nil, txCache) + cmap, _ = xfmrHandlerFunc(inParams) + if cmap != nil && len(cmap) > 0 { + resultMap = cmap + } + break + } + err = yangDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, IsValidate, txCache) + if err != nil { + log.Infof("Empty container(\"%v\").\r\n", uri) + } + break + } else if yangType == YANG_LIST { + if len(xYangSpecMap[reqXpath].xfmrFunc) > 0 { + inParams := formXfmrInputRequest(dbs[cdb], dbs, cdb, ygRoot, uri, GET, "", dbDataMap, nil, txCache) + cmap, _ := xfmrHandlerFunc(inParams) + if cmap != nil && len(cmap) > 0 { + resultMap = cmap + } else { + log.Infof("Empty list(\"%v\").\r\n", uri) + } + } else { + err = yangListDataFill(dbs, ygRoot, uri, reqXpath, dbDataMap, resultMap, tableName, keyName, cdb, IsValidate, txCache) + if err != nil { + log.Infof("yangListDataFill failed for list case(\"%v\").\r\n", uri) + } + } + 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..e17fa0fecb --- /dev/null +++ b/src/translib/transformer/xlate_to_db.go @@ -0,0 +1,658 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "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" { + if len(result[tableName][dbKey].Field) == 0 { + result[tableName][dbKey].Field["NULL"] = "NULL" + } + return + } + + result[tableName][dbKey].Field[field] = value + if _, ok = result[tableName][dbKey].Field["NULL"]; ok { + delete(result[tableName][dbKey].Field, "NULL") + } + 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{}, tblXpathMap map[string][]string, txCache 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") + } + + tableName := "" + if xpathInfo.xfmrTbl != nil { + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, uri, oper, "", nil, "", txCache) + // expecting only one table name from tbl-xfmr + tableName, err = tblNameFromTblXfmrGet(*xYangSpecMap[xpath].xfmrTbl, inParams) + if err != nil { + return err + } + tblXpathMap[tableName] = append(tblXpathMap[tableName], xpathPrefix) + } else { + tableName = *xpathInfo.tableName + } + if xpathInfo.isKey { + dataToDBMapAdd(tableName, dbKey, result, "NONE", "NULL") + return nil + } + + mapFillDataUtil(d, ygRoot, oper, uri, xpath, tableName, dbKey, result, name, value, txCache); + return nil +} + +func mapFillDataUtil(d *db.DB, ygRoot *ygot.GoStruct, oper int, uri string, xpath string, tableName string, dbKey string, result map[string]map[string]db.Value, name string, value interface{}, txCache interface{}) error { + var dbs [db.MaxDB]*db.DB + xpathInfo := xYangSpecMap[xpath] + + 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, txCache) + 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, txCache interface{}) 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, txCache) + log.Infof("Delete req: path(\"%v\"), key(\"%v\"), xpathPrefix(\"%v\"), tableName(\"%v\").", path, keyName, xpathPrefix, tableName) + spec, ok := xYangSpecMap[xpathPrefix] + if ok { + if len(spec.xfmrFunc) > 0 { + var dbs [db.MaxDB]*db.DB + cdb := spec.dbIndex + inParams := formXfmrInputRequest(d, dbs, cdb, ygRoot, path, oper, "", nil, nil, txCache) + ret, err := XlateFuncCall(yangToDbXfmrFunc(spec.xfmrFunc), inParams) + if err == nil { + mapCopy(result, ret[0].Interface().(map[string]map[string]db.Value)) + } else { + return err + } + } else if len(tableName) > 0 { + result[tableName] = make(map[string]db.Value) + if len(keyName) > 0 { + result[tableName][keyName] = db.Value{Field: make(map[string]string)} + if spec.yangEntry != nil && spec.yangEntry.Node.Statement().Keyword == "leaf" { + result[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, txCache interface{}) error { + log.Infof("Update/replace req: path(\"%v\").", path) + dbMapCreate(d, ygRoot, oper, path, jsonData, result, txCache) + log.Infof("Update/replace req: path(\"%v\") result(\"%v\").", path, result) + printDbData(result, "/tmp/yangToDbDataUpRe.txt") + return nil +} + +func dbMapDefaultFieldValFill(d *db.DB, ygRoot *ygot.GoStruct, oper int, uri string, result map[string]map[string]db.Value, yangXpathList []string, tblName string, dbKey string, txCache interface{}) error { + tblData := result[tblName] + var dbs [db.MaxDB]*db.DB + for _, yangXpath := range yangXpathList { + yangNode, ok := xYangSpecMap[yangXpath] + if ok { + for childName := range yangNode.yangEntry.Dir { + childXpath := yangXpath + "/" + childName + childNode, ok := xYangSpecMap[childXpath] + if ok { + if childNode.yangDataType == YANG_LIST || childNode.yangDataType == YANG_CONTAINER { + var tblList []string + tblList = append(tblList, childXpath) + dbMapDefaultFieldValFill(d, ygRoot, oper, uri, result, tblList, tblName, dbKey, txCache) + } + if (childNode.tableName != nil && *childNode.tableName == tblName) || (childNode.xfmrTbl != nil) { + _, ok := tblData[dbKey].Field[childName] + if !ok && len(childNode.defVal) > 0 && len(childNode.fieldName) > 0 { + log.Infof("Update(\"%v\") default: tbl[\"%v\"]key[\"%v\"]fld[\"%v\"] = val(\"%v\").", + childXpath, tblName, dbKey, childNode.fieldName, childNode.defVal) + if len(childNode.xfmrFunc) > 0 { + childYangType := childNode.yangEntry.Type.Kind + _, defValPtr, err := DbToYangType(childYangType, childXpath, childNode.defVal) + if err == nil && defValPtr != nil { + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, childXpath, oper, "", nil, defValPtr, txCache) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xYangSpecMap[childXpath].xfmrFunc), inParams) + if err == nil { + retData := ret[0].Interface().(map[string]string) + for f, v := range retData { + dataToDBMapAdd(tblName, dbKey, result, f, v) + } + } + } else { + log.Infof("Failed to update(\"%v\") default: tbl[\"%v\"]key[\"%v\"]fld[\"%v\"] = val(\"%v\").", + childXpath, tblName, dbKey, childNode.fieldName, childNode.defVal) + } + } else { + mapFillDataUtil(d, ygRoot, oper, uri, childXpath, tblName, dbKey, result, childName, childNode.defVal, txCache) + } + } + } + } + } + } + } + return nil +} + +func dbMapDefaultValFill(d *db.DB, ygRoot *ygot.GoStruct, oper int, uri string, result map[string]map[string]db.Value, tblXpathMap map[string][]string, txCache interface{}) error { + for tbl, tblData := range result { + for dbKey, _ := range tblData { + yxpathList := xDbSpecMap[tbl].yangXpath + if _, ok := tblXpathMap[tbl]; ok { + yxpathList = tblXpathMap[tbl] + } + dbMapDefaultFieldValFill(d, ygRoot, oper, uri, result, yxpathList, tbl, dbKey, txCache) + } + } + 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, txCache interface{}) error { + var err error + tblXpathMap := make(map[string][]string) + + root := xpathRootNameGet(path) + if isSonicYang(path) { + err = sonicYangReqToDbMapCreate(jsonData, result) + } else { + err = yangReqToDbMapCreate(d, ygRoot, oper, root, "", "", jsonData, result, tblXpathMap, txCache) + } + if err == nil { + if !isSonicYang(path) { + xpath, _ := XfmrRemoveXPATHPredicates(path) + yangNode, ok := xYangSpecMap[xpath] + if ok && yangNode.yangDataType != YANG_LEAF && (oper == CREATE || oper == REPLACE) { + log.Infof("Fill default value for %v, oper(%v)\r\n", path, oper) + dbMapDefaultValFill(d, ygRoot, oper, path, result, tblXpathMap, txCache) + } + 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, txCache) + 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) + 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, tblXpathMap map[string][]string, txCache interface{}) 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, txCache) + 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, tblXpathMap, txCache) + } + } 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, txCache) + ret, err := XlateFuncCall(yangToDbXfmrFunc(xYangSpecMap[xpath].xfmrKey), inParams) + if err != nil { + return err + } + if ret != nil { + curKey = ret[0].Interface().(string) + } + } else if ok && xYangSpecMap[xpath].keyName != nil { + curKey = *xYangSpecMap[xpath].keyName + } + + if ok && (typeOfValue == reflect.Map || typeOfValue == reflect.Slice) && xYangSpecMap[xpath].yangDataType != "leaf-list" { + if 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, txCache) + 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, tblXpathMap, txCache) + } + } 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, tblXpathMap, txCache) + if err != nil { + log.Errorf("Failed constructing data for db write: key(\"%v\"), value(\"%v\"), path(\"%v\").", + pathAttr, value, xpathPrefix) + } + } + } + } + } + return nil +} + +/* 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..3f8a56d1e2 --- /dev/null +++ b/src/translib/transformer/xlate_utils.go @@ -0,0 +1,624 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "regexp" + "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, txCache interface{}) (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 + } + } + + keyNameList := yangKeyFromEntryGet(xYangSpecMap[xpath].yangEntry) + id := xYangSpecMap[xpath].keyLevel + keyDataList := strings.SplitN(dbKey, dbKeySep, id) + uriWithKey := fmt.Sprintf("%v", xpath) + uriWithKeyCreate := true + if len(keyDataList) == 0 { + keyDataList = append(keyDataList, dbKey) + } + + /* if uri contins key, use it else use xpath */ + if strings.Contains(uri, "[") { + uriXpath, _ := XfmrRemoveXPATHPredicates(uri) + if (uriXpath == xpath && (strings.HasSuffix(uri, "]") || strings.HasSuffix(uri, "]/"))) { + uriWithKeyCreate = false + } + uriWithKey = fmt.Sprintf("%v", uri) + } + + if len(xYangSpecMap[xpath].xfmrKey) > 0 { + var dbs [db.MaxDB]*db.DB + inParams := formXfmrInputRequest(nil, dbs, db.MaxDB, nil, uri, GET, dbKey, nil, nil, txCache) + ret, err := XlateFuncCall(dbToYangXfmrFunc(xYangSpecMap[xpath].xfmrKey), inParams) + if err != nil { + return nil, "", err + } + rmap := ret[0].Interface().(map[string]interface{}) + if uriWithKeyCreate { + for k, v := range rmap { + uriWithKey += fmt.Sprintf("[%v=%v]", k, v) + } + } + return rmap, uriWithKey, nil + } + + if len(keyNameList) == 0 { + return nil, "", nil + } + + + rmap := make(map[string]interface{}) + if len(keyNameList) > 1 { + log.Infof("No key transformer found for multi element yang key mapping to a single redis key string.") + return rmap, uriWithKey, nil + } + keyXpath := xpath + "/" + keyNameList[0] + xyangSpecInfo, ok := xYangSpecMap[keyXpath] + if !ok || xyangSpecInfo == nil { + errStr := fmt.Sprintf("Failed to find key xpath %v in xYangSpecMap or is nil, needed to fetch the yangEntry data-type", keyXpath) + err = fmt.Errorf("%v", errStr) + return rmap, uriWithKey, err + } + yngTerminalNdDtType := xyangSpecInfo.yangEntry.Type.Kind + resVal, _, err := DbToYangType(yngTerminalNdDtType, keyXpath, keyDataList[0]) + if err != nil { + errStr := fmt.Sprintf("Failure in converting Db value type to yang type for field", keyXpath) + err = fmt.Errorf("%v", errStr) + return rmap, uriWithKey, err + } else { + rmap[keyNameList[0]] = resVal + } + if uriWithKeyCreate { + uriWithKey += fmt.Sprintf("[%v=%v]", keyNameList[0], resVal) + } + + 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{}, txCache 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 + inParams.txCache = txCache + + 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, db.ErrorDB: + 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 +} + + /* Extract key vars, create db key and xpath */ + func xpathKeyExtract(d *db.DB, ygRoot *ygot.GoStruct, oper int, path string, txCache interface{}) (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 + yangXpath, _ := XfmrRemoveXPATHPredicates(curPathWithKey) + _, ok := xYangSpecMap[yangXpath] + if ok { + if strings.Contains(k, "[") { + if len(keyStr) > 0 { + keyStr += keySeparator + } + if len(xYangSpecMap[yangXpath].xfmrKey) > 0 { + xfmrFuncName := yangToDbXfmrFunc(xYangSpecMap[yangXpath].xfmrKey) + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curPathWithKey, oper, "", nil, nil, txCache) + 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] + } + } + } else if len(xYangSpecMap[yangXpath].xfmrKey) > 0 { + xfmrFuncName := yangToDbXfmrFunc(xYangSpecMap[yangXpath].xfmrKey) + inParams := formXfmrInputRequest(d, dbs, db.MaxDB, ygRoot, curPathWithKey, oper, "", nil, nil, txCache) + 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 + } + } + curPathWithKey += "/" + } + curPathWithKey = strings.TrimSuffix(curPathWithKey, "/") + tblPtr := xpathInfo.tableName + if tblPtr != nil { + tableName = *tblPtr + } else if xpathInfo.xfmrTbl != nil { + inParams := formXfmrInputRequest(d, dbs, cdb, ygRoot, curPathWithKey, oper, "", nil, nil, txCache) + tableName, _ = tblNameFromTblXfmrGet(*xpathInfo.xfmrTbl, inParams) + } + return pfxPath, keyStr, tableName + } + + 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) + return xpath, keyStr, tableName + } + cdb = dbInfo.dbIndex + dbOpts := getDBOptions(cdb) + if dbInfo.keyName != nil { + keyStr = *dbInfo.keyName + } else { + 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 + } + +func getYangMdlToSonicMdlList(moduleNm string) []string { + var sncMdlList []string + if xDbSpecTblSeqnMap == nil || len(xDbSpecTblSeqnMap) == 0 { + log.Info("xDbSpecTblSeqnMap is empty.") + return sncMdlList + } + if strings.HasPrefix(moduleNm, SONIC_MDL_PFX) { + sncMdlList = append(sncMdlList, moduleNm) + } else { + //can be optimized if there is a way to know sonic modules, a given OC-Yang spans over + for sncMdl := range(xDbSpecTblSeqnMap) { + sncMdlList = append(sncMdlList, sncMdl) + } + } + return sncMdlList +} + diff --git a/src/translib/transformer/xspec.go b/src/translib/transformer/xspec.go new file mode 100644 index 0000000000..af3fd666a5 --- /dev/null +++ b/src/translib/transformer/xspec.go @@ -0,0 +1,688 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "cvl" + "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 + rpcFunc string + xfmrKey string + keyName *string + dbIndex db.DBNum + keyLevel int + isKey bool + defVal string +} + +type dbInfo struct { + dbIndex db.DBNum + keyName *string + fieldType string + rpcFunc string + dbEntry *yang.Entry + yangXpath []string + module string +} + +type sonicTblSeqnInfo struct { + OrdTbl []string + DepTbl map[string][]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"] } +var xDbSpecTblSeqnMap map[string]*sonicTblSeqnInfo + +/* 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 (xpathData.tableName != nil && *xpathData.tableName != "") { + childToUpdateParent(xpath, *xpathData.tableName) + } + + parentXpathData, ok := xYangSpecMap[xpathPrefix] + /* init current xpath table data with its parent data, change only if needed. */ + if ok && xpathData.tableName == nil { + 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 _, ok := xDbSpecMap[*xpathData.tableName + "/" + entry.Name]; ok { + xpathData.fieldName = entry.Name + } else { + if _, ok := xDbSpecMap[*xpathData.tableName + "/" + strings.ToUpper(entry.Name)]; ok { + xpathData.fieldName = strings.ToUpper(entry.Name) + } + } + } else if xpathData.xfmrTbl != nil { + /* table transformer present */ + xpathData.fieldName = entry.Name + } + } + if xpathData.yangDataType == YANG_LEAF && len(entry.Default) > 0 { + xpathData.defVal = entry.Default + } + + 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, xDbSpecMap map[string]*dbInfo, entry *yang.Entry) { + entryType := entry.Node.Statement().Keyword + + if entry.Name != moduleNm { + if entryType == "container" || entryType == "rpc" { + tableName = entry.Name + } + + if !isYangResType(entryType) { + dbXpath := tableName + if entryType != "container" && entryType != "rpc" { + dbXpath = tableName + "/" + entry.Name + } + xDbSpecMap[dbXpath] = new(dbInfo) + xDbSpecMap[dbXpath].dbIndex = db.MaxDB + xDbSpecMap[dbXpath].dbEntry = entry + xDbSpecMap[dbXpath].fieldType = entryType + xDbSpecMap[dbXpath].module = moduleNm + 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() + case "rpc-callback" : + xDbSpecMap[dbXpath].rpcFunc = 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 + xDbSpecMap[moduleXpath].module = moduleNm + for { + sncTblInfo := new(sonicTblSeqnInfo) + if sncTblInfo == nil { + log.Warningf("Memory allocation failure for storing Tbl order and dependency info for sonic module %v", moduleNm) + break + } + cvlSess, cvlRetSess := cvl.ValidationSessOpen() + if cvlRetSess != cvl.CVL_SUCCESS { + log.Warningf("Failure in creating CVL validation session object required to use CVl API to get Tbl info for module %v - %v", moduleNm, cvlRetSess) + break + } + var cvlRetOrdTbl cvl.CVLRetCode + sncTblInfo.OrdTbl, cvlRetOrdTbl = cvlSess.GetOrderedTables(moduleNm) + if cvlRetOrdTbl != cvl.CVL_SUCCESS { + log.Warningf("Failure in cvlSess.GetOrderedTables(%v) - %v", cvlRetOrdTbl) + + } + sncTblInfo.DepTbl = make(map[string][]string) + if sncTblInfo.DepTbl == nil { + log.Warningf("sncTblInfo.DepTbl is nill , no space to store dependency table list for sonic module %v", moduleNm) + cvl.ValidationSessClose(cvlSess) + break + } + for _, tbl := range(sncTblInfo.OrdTbl) { + var cvlRetDepTbl cvl.CVLRetCode + sncTblInfo.DepTbl[tbl], cvlRetDepTbl = cvlSess.GetDepTables(moduleNm, tbl) + if cvlRetDepTbl != cvl.CVL_SUCCESS { + log.Warningf("Failure in cvlSess.GetDepTables(%v, %v) - %v", moduleNm, tbl, cvlRetDepTbl) + } + + + } + xDbSpecTblSeqnMap[moduleNm] = sncTblInfo + cvl.ValidationSessClose(cvlSess) + break + } + + } + + var childList []string + for _, k := range entry.DirOKeys { + childList = append(childList, k) + } + + + for _, child := range childList { + childPath := tableName + "/" + entry.Dir[child].Name + dbMapFill(tableName, childPath, moduleNm, 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) + xDbSpecTblSeqnMap = make(map[string]*sonicTblSeqnInfo) + + for _, e := range entries { + if e == nil || len(e.Dir) == 0 { + continue + } + moduleNm := e.Name + dbMapFill("", "", moduleNm, 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 + } + + parentXpathData := xYangSpecMap[parent] + if !contains(parentXpathData.childTable, tableName) { + parentXpathData.childTable = append(parentXpathData.childTable, tableName) + } + + if parentXpathData.yangEntry != nil && parentXpathData.yangEntry.Node.Statement().Keyword == "list" && + (parentXpathData.tableName != nil || parentXpathData.xfmrTbl != nil) { + 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) + 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 "rpc-callback" : + xpathData.rpcFunc = 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 if ext.NName() == "ERROR_DB" { + xpathData.dbIndex = db.ErrorDB + } 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" and "rpc" type only + listName := strings.Split(dbXpath, "/") + if len(listName) < 3 { + // check rpc? + rpcName := strings.Split(listName[1], ":") + if len(rpcName) < 2 { + log.Errorf("DB spec-map data not found(%v) \r\n", rpcName) + return err + } + dbXpathData, ok = xDbSpecMap[rpcName[1]] + if ok && dbXpathData.fieldType == "rpc" { + log.Infof("Annotate dbSpecMap for (%v)(rpcName:%v)\r\n", dbXpath, listName[1]) + 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 "rpc-callback" : + dbXpathData.rpcFunc = ext.NName() + default : + } + } + } + dbMapPrint("/tmp/dbSpecMapFull.txt") + return err + } else { + log.Errorf("DB spec-map data not found(%v) \r\n", dbXpath) + return err + } + } + + // list + 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 if ext.NName() == "ERROR_DB" { + dbXpathData.dbIndex = db.ErrorDB + } 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 defVal : %v", d.defVal) + 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 rpcFunc : %v", d.rpcFunc) + 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, " rpcFunc :%v \r\n", v.rpcFunc) + fmt.Fprintf(fp, " module :%v \r\n", v.module) + fmt.Fprintf(fp, " KeyName: ") + if v.keyName != nil { + fmt.Fprintf(fp, "%v", *v.keyName) + } + for _, yxpath := range v.yangXpath { + fmt.Fprintf(fp, "\r\n oc-yang :%v \r\n", yxpath) + } + 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) + } + } + } + } + xDbSpecOrdTblMap[module] = children +} + +func updateChildTable(keyspec []KeySpec, chlist *[]string) ([]string) { + for _, ks := range keyspec { + if (ks.Ts.Name != "") { + if !contains(*chlist, ks.Ts.Name) { + *chlist = append(*chlist, ks.Ts.Name) + } + } + *chlist = updateChildTable(ks.Child, chlist) + } + return *chlist +} diff --git a/src/translib/translib.go b/src/translib/translib.go new file mode 100644 index 0000000000..7d2fe71f73 --- /dev/null +++ b/src/translib/translib.go @@ -0,0 +1,1152 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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. + +to be consumed by the north bound management server implementations + +This package take care of translating the incoming requests to + +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. +*/ + +package translib + +import ( + "errors" + "sync" + "translib/db" + "translib/tlerr" + "github.com/Workiva/go-datastructures/queue" + log "github.com/golang/glog" +) + +//Write lock for all write operations to be synchronized +var writeMutex = &sync.Mutex{} + +//minimum global interval for subscribe in secs +var minSubsInterval = 20 +var maxSubsInterval = 60 + +type ErrSource int + +const ( + ProtoErr ErrSource = iota + AppErr +) + +type SetRequest struct { + Path string + Payload []byte + User string +} + +type SetResponse struct { + ErrSrc ErrSource + Err error +} + +type GetRequest struct { + Path string + User string +} + +type GetResponse struct { + Payload []byte + ErrSrc ErrSource +} + +type ActionRequest struct { + Path string + Payload []byte + User string +} + +type ActionResponse struct { + Payload []byte + ErrSrc ErrSource +} + +type BulkRequest struct { + DeleteRequest []SetRequest + ReplaceRequest []SetRequest + UpdateRequest []SetRequest + CreateRequest []SetRequest + User string +} + +type BulkResponse struct { + DeleteResponse []SetResponse + ReplaceResponse []SetResponse + UpdateResponse []SetResponse + CreateResponse []SetResponse +} + +type SubscribeRequest struct { + Paths []string + Q *queue.PriorityQueue + Stop chan struct{} + User string +} + +type SubscribeResponse struct { + Path string + Payload []byte + Timestamp int64 + SyncComplete bool + IsTerminated bool +} + +type NotificationType int + +const ( + Sample NotificationType = iota + OnChange +) + +type IsSubscribeRequest struct { + Paths []string + User string +} + +type IsSubscribeResponse struct { + Path string + IsOnChangeSupported bool + MinInterval int + Err error + PreferredType NotificationType +} + +type ModelData struct { + Name string + Org string + Ver string +} + +type notificationOpts struct { + mInterval int + pType NotificationType // for TARGET_DEFINED +} + +//initializes logging and app modules +func init() { + log.Flush() +} + +//Creates entries in the redis DB pertaining to the path and payload +func Create(req SetRequest) (SetResponse, error) { + var keys []db.WatchKeys + var resp SetResponse + + if (!isUserAuthorizedForSet(req.User)) { + resp.ErrSrc = ProtoErr + return resp, errors.New("User is not authorized to perform this operation") + } + + path := req.Path + payload := req.Payload + + log.Info("Create request received with path =", path) + log.Info("Create request received with payload =", string(payload)) + + app, appInfo, err := getAppModule(path) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + err = appInitialize(app, appInfo, path, &payload, CREATE) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + writeMutex.Lock() + defer writeMutex.Unlock() + + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + defer d.DeleteDB() + + keys, err = (*app).translateCreate(d) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + err = d.StartTx(keys, appInfo.tablesToWatch) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + resp, err = (*app).processCreate(d) + + if err != nil { + d.AbortTx() + resp.ErrSrc = AppErr + return resp, err + } + + err = d.CommitTx() + + if err != nil { + resp.ErrSrc = AppErr + } + + return resp, err +} + +//Updates entries in the redis DB pertaining to the path and payload +func Update(req SetRequest) (SetResponse, error) { + var keys []db.WatchKeys + var resp SetResponse + + if (!isUserAuthorizedForSet(req.User)) { + resp.ErrSrc = ProtoErr + return resp, errors.New("User is not authorized to perform this operation") + } + + path := req.Path + payload := req.Payload + + log.Info("Update request received with path =", path) + log.Info("Update request received with payload =", string(payload)) + + app, appInfo, err := getAppModule(path) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + err = appInitialize(app, appInfo, path, &payload, UPDATE) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + writeMutex.Lock() + defer writeMutex.Unlock() + + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + defer d.DeleteDB() + + keys, err = (*app).translateUpdate(d) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + err = d.StartTx(keys, appInfo.tablesToWatch) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + resp, err = (*app).processUpdate(d) + + if err != nil { + d.AbortTx() + resp.ErrSrc = AppErr + return resp, err + } + + err = d.CommitTx() + + if err != nil { + resp.ErrSrc = AppErr + } + + return resp, err +} + +//Replaces entries in the redis DB pertaining to the path and payload +func Replace(req SetRequest) (SetResponse, error) { + var err error + var keys []db.WatchKeys + var resp SetResponse + + if (!isUserAuthorizedForSet(req.User)) { + resp.ErrSrc = ProtoErr + return resp, errors.New("User is not authorized to perform this operation") + } + + path := req.Path + payload := req.Payload + + log.Info("Replace request received with path =", path) + log.Info("Replace request received with payload =", string(payload)) + + app, appInfo, err := getAppModule(path) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + err = appInitialize(app, appInfo, path, &payload, REPLACE) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + writeMutex.Lock() + defer writeMutex.Unlock() + + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + defer d.DeleteDB() + + keys, err = (*app).translateReplace(d) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + err = d.StartTx(keys, appInfo.tablesToWatch) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + resp, err = (*app).processReplace(d) + + if err != nil { + d.AbortTx() + resp.ErrSrc = AppErr + return resp, err + } + + err = d.CommitTx() + + if err != nil { + resp.ErrSrc = AppErr + } + + return resp, err +} + +//Deletes entries in the redis DB pertaining to the path +func Delete(req SetRequest) (SetResponse, error) { + var err error + var keys []db.WatchKeys + var resp SetResponse + + if (!isUserAuthorizedForSet(req.User)) { + resp.ErrSrc = ProtoErr + return resp, errors.New("User is not authorized to perform this operation") + } + + path := req.Path + + log.Info("Delete request received with path =", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + err = appInitialize(app, appInfo, path, nil, DELETE) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + writeMutex.Lock() + defer writeMutex.Unlock() + + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) + + if err != nil { + resp.ErrSrc = ProtoErr + return resp, err + } + + defer d.DeleteDB() + + keys, err = (*app).translateDelete(d) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + err = d.StartTx(keys, appInfo.tablesToWatch) + + if err != nil { + resp.ErrSrc = AppErr + return resp, err + } + + resp, err = (*app).processDelete(d) + + if err != nil { + d.AbortTx() + resp.ErrSrc = AppErr + return resp, err + } + + err = d.CommitTx() + + if err != nil { + resp.ErrSrc = AppErr + } + + return resp, err +} + +//Gets data from the redis DB and converts it to northbound format +func Get(req GetRequest) (GetResponse, error) { + var payload []byte + var resp GetResponse + + if (!isUserAuthorizedForGet(req.User)) { + resp = GetResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, errors.New("User is not authorized to perform this operation") + } + + path := req.Path + + log.Info("Received Get request for path = ", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + resp = GetResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, err + } + + err = appInitialize(app, appInfo, path, nil, GET) + + if err != nil { + resp = GetResponse{Payload: payload, ErrSrc: AppErr} + return resp, err + } + + isGetCase := true + dbs, err := getAllDbs(isGetCase) + + if err != nil { + resp = GetResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, err + } + + defer closeAllDbs(dbs[:]) + + err = (*app).translateGet(dbs) + + if err != nil { + resp = GetResponse{Payload: payload, ErrSrc: AppErr} + return resp, err + } + + resp, err = (*app).processGet(dbs) + + return resp, err +} + +func Action(req ActionRequest) (ActionResponse, error) { + var payload []byte + var resp ActionResponse + + if (!isUserAuthorizedForAction(req.User)) { + resp = ActionResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, errors.New("User is not authorized to perform this operation") + } + + path := req.Path + + log.Info("Received Action request for path = ", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + resp = ActionResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, err + } + + aInfo := *appInfo + + aInfo.isNative = true + + err = appInitialize(app, &aInfo, path, &req.Payload, GET) + + if err != nil { + resp = ActionResponse{Payload: payload, ErrSrc: AppErr} + return resp, err + } + + isGetCase := false + dbs, err := getAllDbs(isGetCase) + + if err != nil { + resp = ActionResponse{Payload: payload, ErrSrc: ProtoErr} + return resp, err + } + + defer closeAllDbs(dbs[:]) + + err = (*app).translateAction(dbs) + + if err != nil { + resp = ActionResponse{Payload: payload, ErrSrc: AppErr} + return resp, err + } + + resp, err = (*app).processAction(dbs) + + return resp, err +} + +func Bulk(req BulkRequest) (BulkResponse, error) { + var err error + var keys []db.WatchKeys + var errSrc ErrSource + + delResp := make([]SetResponse, len(req.DeleteRequest)) + replaceResp := make([]SetResponse, len(req.ReplaceRequest)) + updateResp := make([]SetResponse, len(req.UpdateRequest)) + createResp := make([]SetResponse, len(req.CreateRequest)) + + resp := BulkResponse{DeleteResponse: delResp, + ReplaceResponse: replaceResp, + UpdateResponse: updateResp, + CreateResponse: createResp} + + if (!isUserAuthorizedForSet(req.User)) { + return resp, errors.New("User is not authorized to perform this operation") + } + + writeMutex.Lock() + defer writeMutex.Unlock() + + isWriteDisabled := false + d, err := db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) + + if err != nil { + return resp, err + } + + defer d.DeleteDB() + + //Start the transaction without any keys or tables to watch will be added later using AppendWatchTx + err = d.StartTx(nil, nil) + + if err != nil { + return resp, err + } + + for i, _ := range req.DeleteRequest { + path := req.DeleteRequest[i].Path + + log.Info("Delete request received with path =", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + errSrc = ProtoErr + goto BulkDeleteError + } + + err = appInitialize(app, appInfo, path, nil, DELETE) + + if err != nil { + errSrc = AppErr + goto BulkDeleteError + } + + keys, err = (*app).translateDelete(d) + + if err != nil { + errSrc = AppErr + goto BulkDeleteError + } + + err = d.AppendWatchTx(keys, appInfo.tablesToWatch) + + if err != nil { + errSrc = AppErr + goto BulkDeleteError + } + + resp.DeleteResponse[i], err = (*app).processDelete(d) + + if err != nil { + errSrc = AppErr + } + + BulkDeleteError: + + if err != nil { + d.AbortTx() + resp.DeleteResponse[i].ErrSrc = errSrc + resp.DeleteResponse[i].Err = err + return resp, err + } + } + + for i, _ := range req.ReplaceRequest { + path := req.ReplaceRequest[i].Path + payload := req.ReplaceRequest[i].Payload + + log.Info("Replace request received with path =", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + errSrc = ProtoErr + goto BulkReplaceError + } + + log.Info("Bulk replace request received with path =", path) + log.Info("Bulk replace request received with payload =", string(payload)) + + err = appInitialize(app, appInfo, path, &payload, REPLACE) + + if err != nil { + errSrc = AppErr + goto BulkReplaceError + } + + keys, err = (*app).translateReplace(d) + + if err != nil { + errSrc = AppErr + goto BulkReplaceError + } + + err = d.AppendWatchTx(keys, appInfo.tablesToWatch) + + if err != nil { + errSrc = AppErr + goto BulkReplaceError + } + + resp.ReplaceResponse[i], err = (*app).processReplace(d) + + if err != nil { + errSrc = AppErr + } + + BulkReplaceError: + + if err != nil { + d.AbortTx() + resp.ReplaceResponse[i].ErrSrc = errSrc + resp.ReplaceResponse[i].Err = err + return resp, err + } + } + + for i, _ := range req.UpdateRequest { + path := req.UpdateRequest[i].Path + payload := req.UpdateRequest[i].Payload + + log.Info("Update request received with path =", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + errSrc = ProtoErr + goto BulkUpdateError + } + + err = appInitialize(app, appInfo, path, &payload, UPDATE) + + if err != nil { + errSrc = AppErr + goto BulkUpdateError + } + + keys, err = (*app).translateUpdate(d) + + if err != nil { + errSrc = AppErr + goto BulkUpdateError + } + + err = d.AppendWatchTx(keys, appInfo.tablesToWatch) + + if err != nil { + errSrc = AppErr + goto BulkUpdateError + } + + resp.UpdateResponse[i], err = (*app).processUpdate(d) + + if err != nil { + errSrc = AppErr + } + + BulkUpdateError: + + if err != nil { + d.AbortTx() + resp.UpdateResponse[i].ErrSrc = errSrc + resp.UpdateResponse[i].Err = err + return resp, err + } + } + + for i, _ := range req.CreateRequest { + path := req.CreateRequest[i].Path + payload := req.CreateRequest[i].Payload + + log.Info("Create request received with path =", path) + + app, appInfo, err := getAppModule(path) + + if err != nil { + errSrc = ProtoErr + goto BulkCreateError + } + + err = appInitialize(app, appInfo, path, &payload, CREATE) + + if err != nil { + errSrc = AppErr + goto BulkCreateError + } + + keys, err = (*app).translateCreate(d) + + if err != nil { + errSrc = AppErr + goto BulkCreateError + } + + err = d.AppendWatchTx(keys, appInfo.tablesToWatch) + + if err != nil { + errSrc = AppErr + goto BulkCreateError + } + + resp.CreateResponse[i], err = (*app).processCreate(d) + + if err != nil { + errSrc = AppErr + } + + BulkCreateError: + + if err != nil { + d.AbortTx() + resp.CreateResponse[i].ErrSrc = errSrc + resp.CreateResponse[i].Err = err + return resp, err + } + } + + err = d.CommitTx() + + return resp, err +} + +//Subscribes to the paths requested and sends notifications when the data changes in DB +func Subscribe(req SubscribeRequest) ([]*IsSubscribeResponse, error) { + var err error + var sErr error + + paths := req.Paths + q := req.Q + stop := req.Stop + + dbNotificationMap := make(map[db.DBNum][]*notificationInfo) + + resp := make([]*IsSubscribeResponse, len(paths)) + + for i, _ := range resp { + resp[i] = &IsSubscribeResponse{Path: paths[i], + IsOnChangeSupported: false, + MinInterval: minSubsInterval, + PreferredType: Sample, + Err: nil} + } + + if (!isUserAuthorizedForGet(req.User)) { + return resp, errors.New("User is not authorized to perform this operation") + } + + isGetCase := true + dbs, err := getAllDbs(isGetCase) + + if err != nil { + return resp, err + } + + //Do NOT close the DBs here as we need to use them during subscribe notification + + for i, path := range paths { + + app, appInfo, err := getAppModule(path) + + if err != nil { + + if sErr == nil { + sErr = err + } + + resp[i].Err = err + continue + } + + nOpts, nInfo, errApp := (*app).translateSubscribe(dbs, path) + + if errApp != nil { + resp[i].Err = errApp + + if sErr == nil { + sErr = errApp + } + + resp[i].MinInterval = maxSubsInterval + + if nOpts != nil { + if nOpts.mInterval != 0 { + resp[i].MinInterval = nOpts.mInterval + } + + resp[i].PreferredType = nOpts.pType + } + + continue + } else { + + if nOpts != nil { + if nOpts.mInterval != 0 { + resp[i].MinInterval = nOpts.mInterval + } + + resp[i].PreferredType = nOpts.pType + } + + if nInfo == nil { + sErr = tlerr.NotSupportedError{ + Format: "Subscribe not supported", Path: path} + resp[i].Err = sErr + continue + } + + resp[i].IsOnChangeSupported = true + + nInfo.path = path + nInfo.app = app + nInfo.appInfo = appInfo + nInfo.dbs = dbs + + dbNotificationMap[nInfo.dbno] = append(dbNotificationMap[nInfo.dbno], nInfo) + } + + } + + log.Info("map=", dbNotificationMap) + + if sErr != nil { + return resp, sErr + } + + sInfo := &subscribeInfo{syncDone: false, + q: q, + stop: stop} + + sErr = startSubscribe(sInfo, dbNotificationMap) + + return resp, sErr +} + +//Check if subscribe is supported on the given paths +func IsSubscribeSupported(req IsSubscribeRequest) ([]*IsSubscribeResponse, error) { + + paths := req.Paths + resp := make([]*IsSubscribeResponse, len(paths)) + + for i, _ := range resp { + resp[i] = &IsSubscribeResponse{Path: paths[i], + IsOnChangeSupported: false, + MinInterval: minSubsInterval, + PreferredType: Sample, + Err: nil} + } + + if (!isUserAuthorizedForGet(req.User)) { + return resp, errors.New("User is not authorized to perform this operation") + } + + isGetCase := true + dbs, err := getAllDbs(isGetCase) + + if err != nil { + return resp, err + } + + defer closeAllDbs(dbs[:]) + + for i, path := range paths { + + app, _, err := getAppModule(path) + + if err != nil { + resp[i].Err = err + continue + } + + nOpts, _, errApp := (*app).translateSubscribe(dbs, path) + + if errApp != nil { + resp[i].Err = errApp + err = errApp + continue + } else { + resp[i].IsOnChangeSupported = true + + if nOpts != nil { + if nOpts.mInterval != 0 { + resp[i].MinInterval = nOpts.mInterval + } + resp[i].PreferredType = nOpts.pType + } + } + } + + return resp, err +} + +//Gets all the models supported by Translib +func GetModels() ([]ModelData, error) { + var err error + + return getModels(), err +} + +//Creates connection will all the redis DBs. To be used for get request +func getAllDbs(isGetCase bool) ([db.MaxDB]*db.DB, error) { + var dbs [db.MaxDB]*db.DB + var err error + var isWriteDisabled bool + + if isGetCase { + isWriteDisabled = true + } else { + isWriteDisabled = false + } + + //Create Application DB connection + dbs[db.ApplDB], err = db.NewDB(getDBOptions(db.ApplDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + //Create ASIC DB connection + dbs[db.AsicDB], err = db.NewDB(getDBOptions(db.AsicDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + //Create Counter DB connection + dbs[db.CountersDB], err = db.NewDB(getDBOptions(db.CountersDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + //Create Log Level DB connection + dbs[db.LogLevelDB], err = db.NewDB(getDBOptions(db.LogLevelDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + isWriteDisabled = true + + //Create Config DB connection + dbs[db.ConfigDB], err = db.NewDB(getDBOptions(db.ConfigDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + if isGetCase { + isWriteDisabled = true + } else { + isWriteDisabled = false + } + + //Create Flex Counter DB connection + dbs[db.FlexCounterDB], err = db.NewDB(getDBOptions(db.FlexCounterDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + //Create State DB connection + dbs[db.StateDB], err = db.NewDB(getDBOptions(db.StateDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + //Create Error DB connection + dbs[db.ErrorDB], err = db.NewDB(getDBOptions(db.ErrorDB, isWriteDisabled)) + + if err != nil { + closeAllDbs(dbs[:]) + return dbs, err + } + + return dbs, err +} + +//Closes the dbs, and nils out the arr. +func closeAllDbs(dbs []*db.DB) { + for dbsi, d := range dbs { + if d != nil { + d.DeleteDB() + dbs[dbsi] = nil + } + } +} + +// Implement Compare method for priority queue for SubscribeResponse struct +func (val SubscribeResponse) Compare(other queue.Item) int { + o := other.(*SubscribeResponse) + if val.Timestamp > o.Timestamp { + return 1 + } else if val.Timestamp == o.Timestamp { + return 0 + } + return -1 +} + +func getDBOptions(dbNo db.DBNum, isWriteDisabled bool) db.Options { + var opt db.Options + + switch dbNo { + case db.ApplDB, db.CountersDB: + opt = getDBOptionsWithSeparator(dbNo, "", ":", ":", isWriteDisabled) + break + case db.FlexCounterDB, db.AsicDB, db.LogLevelDB, db.ConfigDB, db.StateDB, db.ErrorDB: + opt = getDBOptionsWithSeparator(dbNo, "", "|", "|", isWriteDisabled) + break + } + + return opt +} + +func getDBOptionsWithSeparator(dbNo db.DBNum, initIndicator string, tableSeparator string, keySeparator string, isWriteDisabled bool) db.Options { + return (db.Options{ + DBNo: dbNo, + InitIndicator: initIndicator, + TableNameSeparator: tableSeparator, + KeySeparator: keySeparator, + IsWriteDisabled: isWriteDisabled, + }) +} + +func getAppModule(path string) (*appInterface, *appInfo, error) { + var app appInterface + + aInfo, err := getAppModuleInfo(path) + + if err != nil { + return nil, aInfo, err + } + + app, err = getAppInterface(aInfo.appType) + + if err != nil { + return nil, aInfo, err + } + + return &app, aInfo, err +} + +func appInitialize(app *appInterface, appInfo *appInfo, path string, payload *[]byte, opCode int) error { + var err error + var input []byte + + if payload != nil { + input = *payload + } + + if appInfo.isNative { + log.Info("Native MSFT format") + data := appData{path: path, payload: input} + (*app).initialize(data) + } else { + ygotStruct, ygotTarget, err := getRequestBinder(&path, payload, opCode, &(appInfo.ygotRootType)).unMarshall() + if err != nil { + log.Info("Error in request binding: ", err) + return err + } + + data := appData{path: path, payload: input, ygotRoot: ygotStruct, ygotTarget: ygotTarget} + (*app).initialize(data) + } + + return err +} diff --git a/src/translib/translib_test.go b/src/translib/translib_test.go new file mode 100644 index 0000000000..9ceb1e1a80 --- /dev/null +++ b/src/translib/translib_test.go @@ -0,0 +1,59 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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" + "reflect" + "runtime" + "testing" +) + +// assert fails the test if the condition is false. +func assert(tb testing.TB, condition bool, msg string, v ...interface{}) { + if !condition { + _, file, line, _ := runtime.Caller(1) + fmt.Printf("\033[31m%s:%d: "+msg+"\033[39m\n\n", append([]interface{}{filepath.Base(file), line}, v...)...) + tb.FailNow() + } +} + +// ok fails the test if an err is not nil. +func ok(tb testing.TB, err error) { + if err != nil { + _, file, line, _ := runtime.Caller(1) + fmt.Printf("\033[31m%s:%d: unexpected error: %s\033[39m\n\n", filepath.Base(file), line, err.Error()) + tb.FailNow() + } +} + +// equals fails the test if exp is not equal to act. +func equals(tb testing.TB, exp, act interface{}) { + if !reflect.DeepEqual(exp, act) { + _, file, line, _ := runtime.Caller(1) + fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) + tb.FailNow() + } +} + +func Test_Create(t *testing.T) { + +} diff --git a/src/translib/vlan_intf.go b/src/translib/vlan_intf.go new file mode 100644 index 0000000000..d36ed06c80 --- /dev/null +++ b/src/translib/vlan_intf.go @@ -0,0 +1,149 @@ +////////////////////////////////////////////////////////////////////////// +// +// 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" + log "github.com/golang/glog" + "translib/db" + "translib/tlerr" +) + +/******** CONFIG FUNCTIONS ********/ + +func (app *IntfApp) translateUpdateVlanIntf(d *db.DB, vlanName *string, inpOp reqType) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + + intfObj := app.getAppRootObject() + + m := make(map[string]string) + entryVal := db.Value{Field: m} + entryVal.Field["vlanid"], err = getVlanIdFromVlanName(vlanName) + if err != nil { + return keys, err + } + + vlan := intfObj.Interface[*vlanName] + curr, _ := d.GetEntry(app.vlanD.vlanTs, db.Key{Comp: []string{*vlanName}}) + if !curr.IsPopulated() { + log.Info("VLAN-" + *vlanName + " not present in DB, need to create it!!") + app.ifTableMap[*vlanName] = dbEntry{op: opCreate, entry: entryVal} + return keys, nil + } + app.translateUpdateIntfConfig(vlanName, vlan, &curr) + return keys, err +} + +func (app *IntfApp) processUpdateVlanIntfConfig(d *db.DB) error { + var err error + + for vlanId, vlanEntry := range app.ifTableMap { + switch vlanEntry.op { + case opCreate: + err = d.CreateEntry(app.vlanD.vlanTs, db.Key{Comp: []string{vlanId}}, vlanEntry.entry) + if err != nil { + errStr := "Creating VLAN entry for VLAN : " + vlanId + " failed" + return errors.New(errStr) + } + // Enable STP on newly created Vlans + var vlanList []string + vlanList = append(vlanList, vlanId) + enableStpOnVlanCreation(d, vlanList) + case opUpdate: + err = d.SetEntry(app.vlanD.vlanTs, db.Key{Comp: []string{vlanId}}, vlanEntry.entry) + if err != nil { + errStr := "Updating VLAN entry for VLAN : " + vlanId + " failed" + return errors.New(errStr) + } + } + } + return err +} + +func (app *IntfApp) processUpdateVlanIntf(d *db.DB) error { + var err error + err = app.processUpdateVlanIntfConfig(d) + if err != nil { + return err + } + + return err +} + +/********* DELETE FUNCTIONS ********/ + +func (app *IntfApp) translateDeleteVlanIntf(d *db.DB, vlan *string) ([]db.WatchKeys, error) { + var err error + var keys []db.WatchKeys + curr, err := d.GetEntry(app.vlanD.vlanTs, db.Key{Comp: []string{*vlan}}) + if err != nil { + vlanName := *vlan + vlanId := vlanName[len("Vlan"):len(vlanName)] + errStr := "Invalid Vlan: " + vlanId + return keys, tlerr.InvalidArgsError{Format: errStr} + } + app.ifTableMap[*vlan] = dbEntry{entry: curr, op: opDelete} + return keys, err +} + +func (app *IntfApp) processDeleteVlanIntfAndMembers(d *db.DB) error { + var err error + + for vlanKey, dbentry := range app.ifTableMap { + memberPortsVal, ok := dbentry.entry.Field["members@"] + if ok { + memberPorts := generateMemberPortsSliceFromString(&memberPortsVal) + if memberPorts == nil { + return nil + } + log.Info("MemberPorts = ", memberPortsVal) + + for _, memberPort := range memberPorts { + log.Infof("Member Port:%s part of vlan:%s to be deleted!", memberPort, vlanKey) + err = d.DeleteEntry(app.vlanD.vlanMemberTs, db.Key{Comp: []string{vlanKey, memberPort}}) + if err != nil { + return err + } + } + // Disable STP configuration for ports which are removed from VLan membership + removeStpOnInterfaceSwitchportDeletion(d, memberPorts) + } + // Disable STP configuration for Vlans which are deleted + var vlanList []string + vlanList = append(vlanList, vlanKey) + removeStpConfigOnVlanDeletion(d, vlanList) + + err = d.DeleteEntry(app.vlanD.vlanTs, db.Key{Comp: []string{vlanKey}}) + if err != nil { + return err + } + } + return err +} + +func (app *IntfApp) processDeleteVlanIntf(d *db.DB) error { + var err error + + err = app.processDeleteVlanIntfAndMembers(d) + if err != nil { + return err + } + return err +} diff --git a/tools/.gitkeep b/tools/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/pyang/pyang_plugins/openapi.py b/tools/pyang/pyang_plugins/openapi.py new file mode 100644 index 0000000000..7c793952ba --- /dev/null +++ b/tools/pyang/pyang_plugins/openapi.py @@ -0,0 +1,1024 @@ +################################################################################ +# # +# 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 + +from pyang import plugin +from pyang import statements +import pdb +import yaml +from collections import OrderedDict +import copy +import os +import mmh3 + +# globals +codegenTypesToYangTypesMap = {"int8": {"type":"integer", "format": "int32"}, + "int16": {"type":"integer", "format": "int32"}, + "int32": {"type":"integer", "format": "int32"}, + "int64": {"type":"integer", "format": "int64"}, + "uint8": {"type":"integer", "format": "int32"}, + "uint16": {"type":"integer", "format": "int32"}, + "uint32": {"type":"integer", "format": "int32"}, + "uint64": {"type":"integer", "format": "int64"}, + "decimal64": {"type":"number", "format": "double"}, + "string": {"type":"string"}, + "binary": {"type":"string", "format": "binary"}, + "boolean": {"type":"boolean"}, + "bits": {"type":"integer", "format": "int32"}, + "identityref": {"type":"string"}, + "union": {"type":"string"}, + "counter32": {"type":"integer", "format": "int64"}, + "counter64": {"type":"integer", "format": "int64"}, + "long": {"type":"integer", "format": "int64"}, + } +moduleDict = OrderedDict() +nodeDict = OrderedDict() +XpathToBodyTagDict = OrderedDict() +keysToLeafRefObjSet = set() +currentTag = None +errorList = [] +warnList = [] +verbs = ["post", "put", "patch", "get", "delete"] +responses = { # Common to all verbs + "500": {"description": "Internal Server Error"}, + "401": {"description": "Unauthorized"}, + "405": {"description": "Method Not Allowed"}, + "400": {"description": "Bad request"}, + "415": {"description": "Unsupported Media Type"}, +} +verb_responses = {} +verb_responses["rpc"] = { + "204": {"description": "No Content"}, + "404": {"description": "Not Found"}, + "403": {"description": "Forbidden"}, +} +verb_responses["post"] = { + "201": {"description": "Created"}, + "409": {"description": "Conflict"}, + "404": {"description": "Not Found"}, + "403": {"description": "Forbidden"}, +} +verb_responses["put"] = { + "201": {"description": "Created"}, + "204": {"description": "No Content"}, + "404": {"description": "Not Found"}, + "409": {"description": "Conflict"}, + "403": {"description": "Forbidden"}, +} +verb_responses["patch"] = { + "204": {"description": "No Content"}, + "404": {"description": "Not Found"}, + "409": {"description": "Conflict"}, + "403": {"description": "Forbidden"}, +} +verb_responses["delete"] = { + "204": {"description": "No Content"}, + "404": {"description": "Not Found"}, +} +verb_responses["get"] = { + "200": {"description": "Ok"}, + "404": {"description": "Not Found"}, +} + +def merge_two_dicts(x, y): + z = x.copy() # start with x's keys and values + z.update(y) # modifies z with y's keys and values & returns None + return z + +def ordered_dump(data, stream=None, Dumper=yaml.Dumper, **kwds): + class OrderedDumper(Dumper): + pass + def _dict_representer(dumper, data): + return dumper.represent_mapping( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + data.items()) + OrderedDumper.add_representer(OrderedDict, _dict_representer) + return yaml.dump(data, stream, OrderedDumper, **kwds) + +swaggerDict = OrderedDict() +swaggerDict["swagger"] = "2.0" +swaggerDict["info"] = OrderedDict() +swaggerDict["info"]["description"] = "Network management Open APIs for Broadcom's Sonic." +swaggerDict["info"]["version"] = "1.0.0" +swaggerDict["info"]["title"] = "SONiC Network Management APIs" +swaggerDict["schemes"] = ["https", "http"] +swagger_tags = [] +swaggerDict["tags"] = swagger_tags +swaggerDict["paths"] = OrderedDict() +swaggerDict["definitions"] = OrderedDict() + +def resetSwaggerDict(): + global moduleDict + global nodeDict + global XpathToBodyTagDict + global keysToLeafRefObjSet + global swaggerDict + global swagger_tags + global currentTag + + moduleDict = OrderedDict() + XpathToBodyTagDict = OrderedDict() + keysToLeafRefObjSet = set() + + swaggerDict = OrderedDict() + swaggerDict["swagger"] = "2.0" + swaggerDict["info"] = OrderedDict() + swaggerDict["info"]["description"] = "Network management Open APIs for Sonic." + swaggerDict["info"]["version"] = "1.0.0" + swaggerDict["info"]["title"] = "Sonic Network Management APIs" + swaggerDict["schemes"] = ["https", "http"] + swagger_tags = [] + currentTag = None + swaggerDict["tags"] = swagger_tags + swaggerDict["paths"] = OrderedDict() + swaggerDict["definitions"] = OrderedDict() + +def pyang_plugin_init(): + plugin.register_plugin(OpenApiPlugin()) + +class OpenApiPlugin(plugin.PyangPlugin): + def add_output_format(self, fmts): + self.multiple_modules = True + fmts['swaggerapi'] = self + + def add_opts(self, optparser): + optlist = [ + optparse.make_option("--outdir", + type="string", + dest="outdir", + help="Output directory for specs"), + ] + g = optparser.add_option_group("OpenApiPlugin options") + g.add_options(optlist) + + def setup_fmt(self, ctx): + ctx.implicit_errors = False + + def emit(self, ctx, modules, fd): + + global currentTag + global errorList + global warnList + + if ctx.opts.outdir is None: + print("[Error]: Output directory is not mentioned") + sys.exit(2) + + if not os.path.exists(ctx.opts.outdir): + print("[Error]: Specified outdir: ", ctx.opts.outdir, " does not exists") + sys.exit(2) + + for module in modules: + print("===> processing ", module.i_modulename) + if module.keyword == "submodule": + continue + resetSwaggerDict() + currentTag = module.i_modulename + walk_module(module) + # delete root '/' as we dont support it. + + if len(swaggerDict["paths"]) > 0: + if "/restconf/data/" in swaggerDict["paths"]: + del(swaggerDict["paths"]["/restconf/data/"]) + + if len(swaggerDict["paths"]) <= 0: + continue + + # check if file is same + yamlFn = ctx.opts.outdir + '/' + module.i_modulename + ".yaml" + code = ordered_dump(swaggerDict, Dumper=yaml.SafeDumper) + if os.path.isfile(yamlFn): + f=open(yamlFn,'r') + oldCode = f.read() + if (oldCode==code): + print('code unchanged.. skipping write for file:'+yamlFn) + f.close() + continue + else: + print('code changed.. overwriting file:'+yamlFn) + fout = open(yamlFn,'w') + fout.write(code) + fout.close() + else: + with open(ctx.opts.outdir + '/' + module.i_modulename + ".yaml", "w") as spec: + spec.write(ordered_dump(swaggerDict, Dumper=yaml.SafeDumper)) + + if len(warnList) > 0: + print("========= Warnings observed =======") + for warn in warnList: + print(warn) + + if len(errorList) > 0: + print("========= Errors observed =======") + for err in errorList: + print(err) + print("========= Exiting due to above Errors =======") + sys.exit(2) + +def walk_module(module): + for child in module.i_children: + walk_child(child) + +def add_swagger_tag(module): + if module.i_modulename not in moduleDict: + moduleDict[module.i_modulename] = OrderedDict() + moduleDict[module.i_modulename]["name"] = module.i_modulename + moduleDict[module.i_modulename]["description"] = "Operations for " + module.i_modulename + swagger_tags.append(moduleDict[module.i_modulename]) + else: + return + +def swagger_it(child, defName, pathstr, payload, metadata, verb, operId=False, xParamsList=[]): + + firstEncounter = True + verbPathStr = pathstr + global currentTag + if verb == "post": + pathstrList = pathstr.split('/') + pathstrList.pop() + verbPathStr = "/".join(pathstrList) + if not verbPathStr.startswith("/"): + verbPathStr = "/" + verbPathStr + + verbPathStr = "/restconf/data" + verbPathStr + if verbPathStr not in swaggerDict["paths"]: + swaggerDict["paths"][verbPathStr] = OrderedDict() + + if verb not in swaggerDict["paths"][verbPathStr]: + swaggerDict["paths"][verbPathStr][verb] = OrderedDict() + swaggerDict["paths"][verbPathStr][verb]["tags"] = [currentTag] + if verb != "delete" and verb != "get": + swaggerDict["paths"][verbPathStr][verb]["consumes"] = ["application/yang-data+json"] + swaggerDict["paths"][verbPathStr][verb]["produces"] = ["application/yang-data+json"] + swaggerDict["paths"][verbPathStr][verb]["parameters"] = [] + swaggerDict["paths"][verbPathStr][verb]["responses"] = copy.deepcopy(merge_two_dicts(responses, verb_responses[verb])) + firstEncounter = False + + haveXParams = False + tempParamsList = [] + for entry in xParamsList: + if entry["yangName"] not in tempParamsList: + tempParamsList.append(entry["yangName"]) + else: + haveXParams = True + break + + if haveXParams: + swaggerDict["paths"][verbPathStr][verb]["x-params"] = {"varMapping":copy.deepcopy(xParamsList)} + + opId = None + if "operationId" not in swaggerDict["paths"][verbPathStr][verb]: + if not operId: + swaggerDict["paths"][verbPathStr][verb]["operationId"] = verb + '_' + defName + else: + swaggerDict["paths"][verbPathStr][verb]["operationId"] = operId + + opId = swaggerDict["paths"][verbPathStr][verb]["operationId"] + + desc = child.search_one('description') + if desc is None: + desc = '' + else: + desc = desc.arg + desc = "OperationId: " + opId + "\n" + desc + swaggerDict["paths"][verbPathStr][verb]["description"] = desc + + else: + opId = swaggerDict["paths"][verbPathStr][verb]["operationId"] + + verbPath = swaggerDict["paths"][verbPathStr][verb] + + if not firstEncounter: + for meta in metadata: + metaTag = OrderedDict() + metaTag["in"] = "path" + metaTag["name"] = meta["name"] + metaTag["required"] = True + metaTag["type"] = meta["type"] + if 'enums' in meta: + metaTag["enum"] = meta["enums"] + if hasattr(meta,'format'): + if meta["format"] != "": + metaTag["format"] = meta["format"] + metaTag["description"] = meta["desc"] + verbPath["parameters"].append(metaTag) + + + if verb in ["post", "put", "patch"]: + if not firstEncounter: + bodyTag = OrderedDict() + bodyTag["in"] = "body" + bodyTag["name"] = "body" + bodyTag["required"] = True + bodyTag["schema"] = OrderedDict() + operationDefnName = opId + swaggerDict["definitions"][operationDefnName] = OrderedDict() + swaggerDict["definitions"][operationDefnName]["allOf"] = [] + bodyTag["schema"]["$ref"] = "#/definitions/" + operationDefnName + verbPath["parameters"].append(bodyTag) + swaggerDict["definitions"][operationDefnName]["allOf"].append({"$ref" : "#/definitions/" + defName}) + else: + bodyTag = None + for entry in verbPath["parameters"]: + if entry["name"] == "body" and entry["in"] == "body": + bodyTag = entry + break + operationDefnName = bodyTag["schema"]["$ref"].split('/')[-1] + swaggerDict["definitions"][operationDefnName]["allOf"].append({"$ref" : "#/definitions/" + defName}) + + if verb == "get": + verbPath["responses"]["200"]["schema"] = OrderedDict() + verbPath["responses"]["200"]["schema"]["$ref"] = "#/definitions/" + defName + +def handle_rpc(child, actXpath, pathstr): + global currentTag + verbPathStr = "/restconf/operations" + pathstr + verb = "post" + customName = getOpId(child) + DefName = shortenNodeName(child, customName) + opId = "rpc_" + DefName + add_swagger_tag(child.i_module) + + # build input payload + input_payload = OrderedDict() + input_child = child.search_one('input', None, child.i_children) + if input_child is None: + print("There is no input node for RPC ", "Xpath: ", actXpath) + build_payload(input_child, input_payload, pathstr, True, actXpath, True, False, []) + input_Defn = "rpc_input_" + DefName + swaggerDict["definitions"][input_Defn] = OrderedDict() + swaggerDict["definitions"][input_Defn]["type"] = "object" + swaggerDict["definitions"][input_Defn]["properties"] = copy.deepcopy(input_payload) + + # build output payload + output_payload = OrderedDict() + output_child = child.search_one('output', None, child.i_children) + if output_child is None: + print("There is no output node for RPC ", "Xpath: ", actXpath) + build_payload(output_child, output_payload, pathstr, True, actXpath, True, False, []) + output_Defn = "rpc_output_" + DefName + swaggerDict["definitions"][output_Defn] = OrderedDict() + swaggerDict["definitions"][output_Defn]["type"] = "object" + swaggerDict["definitions"][output_Defn]["properties"] = copy.deepcopy(output_payload) + + if verbPathStr not in swaggerDict["paths"]: + swaggerDict["paths"][verbPathStr] = OrderedDict() + + swaggerDict["paths"][verbPathStr][verb] = OrderedDict() + swaggerDict["paths"][verbPathStr][verb]["tags"] = [currentTag] + + # Set Operation ID + swaggerDict["paths"][verbPathStr][verb]["operationId"] = opId + + # Set Description + desc = child.search_one('description') + if desc is None: + desc = '' + else: + desc = desc.arg + desc = "OperationId: " + opId + "\n" + desc + swaggerDict["paths"][verbPathStr][verb]["description"] = desc + verbPath = swaggerDict["paths"][verbPathStr][verb] + + # Request payload + if len(input_payload[child.i_module.i_modulename + ':input']['properties']) > 0: + verbPath["parameters"] = [] + verbPath["consumes"] = ["application/yang-data+json"] + bodyTag = OrderedDict() + bodyTag["in"] = "body" + bodyTag["name"] = "body" + bodyTag["required"] = True + bodyTag["schema"] = OrderedDict() + bodyTag["schema"]["$ref"] = "#/definitions/" + input_Defn + verbPath["parameters"].append(bodyTag) + + # Response payload + verbPath["responses"] = copy.deepcopy(merge_two_dicts(responses, verb_responses["rpc"])) + if len(output_payload[child.i_module.i_modulename + ':output']['properties']) > 0: + verbPath["produces"] = ["application/yang-data+json"] + verbPath["responses"]["204"]["schema"] = OrderedDict() + verbPath["responses"]["204"]["schema"]["$ref"] = "#/definitions/" + output_Defn + +def walk_child(child): + global XpathToBodyTagDict + customName = None + + actXpath = statements.mk_path_str(child, True) + metadata = [] + keyNodesInPath = [] + paramsList = [] + pathstr = mk_path_refine(child, metadata, keyNodesInPath, False, paramsList) + + if actXpath in keysToLeafRefObjSet: + return + + if child.keyword == "rpc": + add_swagger_tag(child.i_module) + handle_rpc(child, actXpath, pathstr) + return + + if child.keyword in ["list", "container", "leaf", "leaf-list"]: + payload = OrderedDict() + + add_swagger_tag(child.i_module) + build_payload(child, payload, pathstr, True, actXpath, True, False, []) + + if len(payload) == 0 and child.i_config == True: + return + + if child.keyword == "leaf" or child.keyword == "leaf-list": + if hasattr(child, 'i_is_key'): + if child.i_leafref is not None: + listKeyPath = statements.mk_path_str(child.i_leafref_ptr[0], True) + if listKeyPath not in keysToLeafRefObjSet: + keysToLeafRefObjSet.add(listKeyPath) + return + + customName = getOpId(child) + defName = shortenNodeName(child, customName) + + if child.i_config == False: + payload_get = OrderedDict() + build_payload(child, payload_get, pathstr, True, actXpath, True, True, []) + if len(payload_get) == 0: + return + + defName_get = "get" + '_' + defName + swaggerDict["definitions"][defName_get] = OrderedDict() + swaggerDict["definitions"][defName_get]["type"] = "object" + swaggerDict["definitions"][defName_get]["properties"] = copy.deepcopy(payload_get) + swagger_it(child, defName_get, pathstr, payload_get, metadata, "get", defName_get, paramsList) + else: + swaggerDict["definitions"][defName] = OrderedDict() + swaggerDict["definitions"][defName]["type"] = "object" + swaggerDict["definitions"][defName]["properties"] = copy.deepcopy(payload) + + for verb in verbs: + if child.keyword == "leaf-list": + metadata_leaf_list = [] + keyNodesInPath_leaf_list = [] + paramsLeafList = [] + pathstr_leaf_list = mk_path_refine(child, metadata_leaf_list, keyNodesInPath_leaf_list, True, paramsLeafList) + + if verb == "get": + payload_get = OrderedDict() + build_payload(child, payload_get, pathstr, True, actXpath, True, True, []) + if len(payload_get) == 0: + continue + defName_get = "get" + '_' + defName + swaggerDict["definitions"][defName_get] = OrderedDict() + swaggerDict["definitions"][defName_get]["type"] = "object" + swaggerDict["definitions"][defName_get]["properties"] = copy.deepcopy(payload_get) + swagger_it(child, defName_get, pathstr, payload_get, metadata, verb, defName_get, paramsList) + + if child.keyword == "leaf-list": + defName_get_leaf_list = "get" + '_llist_' + defName + swagger_it(child, defName_get, pathstr_leaf_list, payload_get, metadata_leaf_list, verb, defName_get_leaf_list, paramsLeafList) + + continue + + if verb == "post" and child.keyword == "list": + continue + + if verb == "delete" and child.keyword == "container": + # Check to see if any of the child is part of + # key list, if so skip delete operation + if isUriKeyInPayload(child,keyNodesInPath): + continue + + swagger_it(child, defName, pathstr, payload, metadata, verb, False, paramsList) + if verb == "delete" and child.keyword == "leaf-list": + defName_del_leaf_list = "del" + '_llist_' + defName + swagger_it(child, defName, pathstr_leaf_list, payload, metadata_leaf_list, verb, defName_del_leaf_list, paramsLeafList) + + if child.keyword == "list": + listMetaData = copy.deepcopy(metadata) + listparamsList = copy.deepcopy(paramsList) + walk_child_for_list_base(child,actXpath,pathstr, listMetaData, defName, listparamsList) + + if hasattr(child, 'i_children'): + for ch in child.i_children: + walk_child(ch) + +def walk_child_for_list_base(child, actXpath, pathstr, metadata, nonBaseDefName=None, paramsList=[]): + + payload = OrderedDict() + pathstrList = pathstr.split('/') + + lastNode = pathstrList[-1] + nodeName = lastNode.split('=')[0] + pathstrList.pop() + pathstrList.append(nodeName) + + verbPathStr = "/".join(pathstrList) + if not verbPathStr.startswith("/"): + pathstr = "/" + verbPathStr + else: + pathstr = verbPathStr + + for key in child.i_key: + metadata.pop() + if len(paramsList) > 0: + paramsList.pop() + + add_swagger_tag(child.i_module) + build_payload(child, payload, pathstr, False, "", True, False, []) + + if len(payload) == 0 and child.i_config == True: + return + + customName = getOpId(child) + defName = shortenNodeName(child, customName) + defName = "list"+'_'+defName + + if child.i_config == False: + + payload_get = OrderedDict() + build_payload(child, payload_get, pathstr, False, "", True, True, []) + + if len(payload_get) == 0: + return + + defName_get = "get" + '_' + defName + if nonBaseDefName is not None: + swagger_it(child, "get" + '_' + nonBaseDefName, pathstr, payload_get, metadata, "get", defName_get, paramsList) + else: + swaggerDict["definitions"][defName_get] = OrderedDict() + swaggerDict["definitions"][defName_get]["type"] = "object" + swaggerDict["definitions"][defName_get]["properties"] = copy.deepcopy(payload_get) + swagger_it(child, defName_get, pathstr, payload_get, metadata, "get", defName_get, paramsList) + else: + if nonBaseDefName is None: + swaggerDict["definitions"][defName] = OrderedDict() + swaggerDict["definitions"][defName]["type"] = "object" + swaggerDict["definitions"][defName]["properties"] = copy.deepcopy(payload) + + for verb in verbs: + if verb == "get": + payload_get = OrderedDict() + build_payload(child, payload_get, pathstr, False, "", True, True, []) + + if len(payload_get) == 0: + continue + + defName_get = "get" + '_' + defName + if nonBaseDefName is not None: + swagger_it(child, "get" + '_' + nonBaseDefName, pathstr, payload_get, metadata, verb, defName_get, paramsList) + else: + swaggerDict["definitions"][defName_get] = OrderedDict() + swaggerDict["definitions"][defName_get]["type"] = "object" + swaggerDict["definitions"][defName_get]["properties"] = copy.deepcopy(payload_get) + swagger_it(child, defName_get, pathstr, payload_get, metadata, verb, defName_get, paramsList) + continue + + if nonBaseDefName is not None: + swagger_it(child, nonBaseDefName, pathstr, payload, metadata, verb, verb + '_' + defName, paramsList) + else: + swagger_it(child, defName, pathstr, payload, metadata, verb, verb + '_' + defName, paramsList) + +def build_payload(child, payloadDict, uriPath="", oneInstance=False, Xpath="", firstCall=False, config_false=False, moduleList=[]): + + nodeModuleName = child.i_module.i_modulename + if nodeModuleName not in moduleList: + moduleList.append(nodeModuleName) + firstCall = True + + global keysToLeafRefObjSet + + if child.i_config == False and not config_false: + return # temporary + + chs=[] + try: + chs = [ch for ch in child.i_children + if ch.keyword in statements.data_definition_keywords] + except: + # do nothing as it could be due to i_children not present + pass + + childJson = None + if child.keyword == "container" and len(chs) > 0: + if firstCall: + nodeName = child.i_module.i_modulename + ':' + child.arg + else: + nodeName = child.arg + payloadDict[nodeName] = OrderedDict() + payloadDict[nodeName]["type"] = "object" + payloadDict[nodeName]["properties"] = OrderedDict() + childJson = payloadDict[nodeName]["properties"] + + elif child.keyword == "list" and len(chs) > 0: + if firstCall: + nodeName = child.i_module.i_modulename + ':' + child.arg + else: + nodeName = child.arg + payloadDict[nodeName] = OrderedDict() + returnJson = None + + payloadDict[nodeName]["type"] = "array" + payloadDict[nodeName]["items"] = OrderedDict() + payloadDict[nodeName]["items"]["type"] = "object" + payloadDict[nodeName]["items"]["required"] = [] + + for listKey in child.i_key: + payloadDict[nodeName]["items"]["required"].append(listKey.arg) + + payloadDict[nodeName]["items"]["properties"] = OrderedDict() + returnJson = payloadDict[nodeName]["items"]["properties"] + + childJson = returnJson + + elif child.keyword == "leaf": + + if firstCall: + nodeName = child.i_module.i_modulename + ':' + child.arg + else: + nodeName = child.arg + payloadDict[nodeName] = OrderedDict() + typeInfo = getType(child) + enums = None + if isinstance(typeInfo, tuple): + enums = typeInfo[1] + typeInfo = typeInfo[0] + + if 'type' in typeInfo: + dType = typeInfo["type"] + else: + dType = "string" + + payloadDict[nodeName]["type"] = dType + if enums is not None: + payloadDict[nodeName]["enum"] = enums + + if 'format' in typeInfo: + payloadDict[nodeName]["format"] = typeInfo["format"] + + elif child.keyword == "leaf-list": + + if firstCall: + nodeName = child.i_module.i_modulename + ':' + child.arg + else: + nodeName = child.arg + + payloadDict[nodeName] = OrderedDict() + payloadDict[nodeName]["type"] = "array" + payloadDict[nodeName]["items"] = OrderedDict() + + typeInfo = getType(child) + enums = None + if isinstance(typeInfo, tuple): + enums = typeInfo[1] + typeInfo = typeInfo[0] + + if 'type' in typeInfo: + dType = typeInfo["type"] + else: + dType = "string" + + payloadDict[nodeName]["items"]["type"] = dType + if enums is not None: + payloadDict[nodeName]["items"]["enum"] = enums + + if 'format' in typeInfo: + payloadDict[nodeName]["items"]["format"] = typeInfo["format"] + + elif child.keyword == "choice" or child.keyword == "case": + childJson = payloadDict + + elif child.keyword == "input" or child.keyword == "output": + if firstCall: + nodeName = child.i_module.i_modulename + ':' + child.keyword + else: + nodeName = child.keyword + + payloadDict[nodeName] = OrderedDict() + payloadDict[nodeName]["type"] = "object" + payloadDict[nodeName]["properties"] = OrderedDict() + childJson = payloadDict[nodeName]["properties"] + + if hasattr(child, 'i_children'): + for ch in child.i_children: + build_payload(ch,childJson,uriPath, False, Xpath, False, config_false, copy.deepcopy(moduleList)) + +def handleDuplicateParams(node, paramMeta={}): + paramNamesList = paramMeta["paramNamesList"] + paramsList = paramMeta["paramsList"] + paramName = node.arg + paramNamesList.append(paramName) + paramNameCount = paramNamesList.count(paramName) + paramDictEntry = OrderedDict() + + if paramNameCount > 1: + origParamName = paramName + paramName = paramName + str(paramNameCount-1) + while paramName in paramNamesList: + paramNameCount = paramNameCount + 1 + paramName = origParamName + str(paramNameCount-1) + paramNamesList.append(paramName) + + paramDictEntry["uriName"] = paramName + paramDictEntry["yangName"] = node.arg + if paramName != node.arg: + paramMeta["sameParams"] = True + paramsList.append(paramDictEntry) + + return paramName + +def mk_path_refine(node, metadata, keyNodes=[], restconf_leaflist=False, paramsList=[]): + paramMeta={} + paramMeta["paramNamesList"] = [] + paramMeta["paramsList"] = [] + paramMeta["sameParams"] = False + + def mk_path(node): + """Returns the XPath path of the node""" + if node.keyword in ['choice', 'case']: + return mk_path(node.parent) + def name(node): + extra = "" + if node.keyword == "leaf-list" and restconf_leaflist: + extraKeys = [] + paramName = handleDuplicateParams(node,paramMeta) + extraKeys.append('{' + paramName + '}') + desc = node.search_one('description') + if desc is None: + desc = '' + else: + desc = desc.arg + metaInfo = OrderedDict() + metaInfo["desc"] = desc + metaInfo["name"] = paramName + metaInfo["type"] = "string" + metaInfo["format"] = "" + metadata.append(metaInfo) + extra = ",".join(extraKeys) + + if node.keyword == "list": + extraKeys = [] + for index, list_key in enumerate(node.i_key): + keyNodes.append(list_key) + if list_key.i_leafref is not None: + keyNodes.append(list_key.i_leafref_ptr[0]) + paramName = handleDuplicateParams(list_key,paramMeta) + extraKeys.append('{' + paramName + '}') + desc = list_key.search_one('description') + if desc is None: + desc = '' + else: + desc = desc.arg + metaInfo = OrderedDict() + metaInfo["desc"] = desc + metaInfo["name"] = paramName + typeInfo = getType(list_key) + + if isinstance(typeInfo, tuple): + metaInfo["enums"] = typeInfo[1] + typeInfo = typeInfo[0] + + if 'type' in typeInfo: + dType = typeInfo["type"] + else: + dType = "string" + + metaInfo["type"] = dType + + if 'format' in typeInfo: + metaInfo["format"] = typeInfo["format"] + else: + metaInfo["format"] = "" + + metadata.append(metaInfo) + extra = ",".join(extraKeys) + + if len(extra) > 0: + xpathToReturn = node.i_module.i_modulename + ':' + node.arg + '=' + extra + else: + xpathToReturn = node.i_module.i_modulename + ':' + node.arg + return xpathToReturn + + if node.parent.keyword in ['module', 'submodule']: + return "/" + name(node) + else: + p = mk_path(node.parent) + return p + "/" + name(node) + + xpath = mk_path(node) + module_name = "" + final_xpathList = [] + for path in xpath.split('/')[1:]: + mod_name, node_name = path.split(':') + if mod_name != module_name: + final_xpathList.append(path) + module_name = mod_name + else: + final_xpathList.append(node_name) + + xpath = "/".join(final_xpathList) + if not xpath.startswith('/'): + xpath = '/' + xpath + + if paramMeta["sameParams"]: + for entry in paramMeta["paramsList"]: + paramsList.append(copy.deepcopy(entry)) + + return xpath + +def handle_leafref(node,xpath): + path_type_spec = node.i_leafref + target_node = path_type_spec.i_target_node + if target_node.keyword in ["leaf", "leaf-list"]: + return getType(target_node) + else: + print("leafref not pointing to leaf/leaflist") + sys.exit(2) + +def getOpId(node): + name = None + for substmt in node.substmts: + if substmt.keyword.__class__.__name__ == 'tuple': + if substmt.keyword[0] == 'sonic-extensions': + if substmt.keyword[1] == 'openapi-opid': + name = substmt.arg + return name + +def shortenNodeName(node, overridenName=None): + global nodeDict + global errorList + global warnList + + xpath = statements.mk_path_str(node, False) + xpath_prefix = statements.mk_path_str(node, True) + if overridenName is None: + name = node.i_module.i_modulename + xpath.replace('/','_') + else: + name = overridenName + + name = name.replace('-','_').lower() + if name not in nodeDict: + nodeDict[name] = xpath + else: + if overridenName is None: + while name in nodeDict: + if xpath == nodeDict[name]: + break + name = node.i_module.i_modulename + '_' + name + name = name.replace('-','_').lower() + nodeDict[name] = xpath + else: + if xpath != nodeDict[name]: + print("[Name collision] at ", xpath, " name: ", name, " is used, override using openapi-opid annotation") + sys.exit(2) + if len(name) > 150: + if overridenName is None: + # Generate unique hash + mmhash = mmh3.hash(name, signed=False) + name = node.i_module.i_modulename + str(mmhash) + name = name.replace('-','_').lower() + nodeDict[name] = xpath + warnList.append("[Warn]: Using autogenerated shortened OperId for " + str(xpath_prefix) + " please provide unique manual input through openapi-opid annotation using deviation file if you want to override") + if len(name) > 150: + errorList.append("[Error: ] OpID is too big for " + str(xpath_prefix) +" please provide unique manual input through openapi-opid annotation using deviation file") + return name + +def getCamelForm(moName): + hasHiphen = False + moName = moName.replace('_','-') + if '-' in moName: + hasHiphen = True + + while (hasHiphen): + index = moName.find('-') + if index != -1: + moNameList = list(moName) + # capitalize character hiphen + moNameList[index+1] = moNameList[index+1].upper() + # delete '-' + del(moNameList[index]) + moName = "".join(moNameList) + + if '-' in moName: + hasHiphen = True + else: + hasHiphen = False + else: + break + + return moName + +def getType(node): + + global codegenTypesToYangTypesMap + xpath = statements.mk_path_str(node, True) + + def resolveType(stmt, nodeType): + if nodeType == "string" \ + or nodeType == "instance-identifier" \ + or nodeType == "identityref": + return codegenTypesToYangTypesMap["string"] + elif nodeType == "enumeration": + enums = [] + for enum in stmt.substmts: + if enum.keyword == "enum": + enums.append(enum.arg) + return codegenTypesToYangTypesMap["string"], enums + elif nodeType == "empty" or nodeType == "boolean": + return {"type": "boolean", "format": "boolean"} + elif nodeType == "leafref": + return handle_leafref(node,xpath) + elif nodeType == "union": + return codegenTypesToYangTypesMap["string"] + elif nodeType == "decimal64": + return codegenTypesToYangTypesMap[nodeType] + elif nodeType in ['int8', 'int16', 'int32', 'int64', + 'uint8', 'uint16', 'uint32', 'uint64', 'binary', 'bits']: + return codegenTypesToYangTypesMap[nodeType] + else: + print("no base type found") + sys.exit(2) + + base_types = ['int8', 'int16', 'int32', 'int64', + 'uint8', 'uint16', 'uint32', 'uint64', + 'decimal64', 'string', 'boolean', 'enumeration', + 'bits', 'binary', 'leafref', 'identityref', 'empty', + 'union', 'instance-identifier' + ] + # Get Type of a node + t = node.search_one('type') + + while t.arg not in base_types: + # chase typedef + name = t.arg + if name.find(":") == -1: + prefix = None + else: + [prefix, name] = name.split(':', 1) + if prefix is None or t.i_module.i_prefix == prefix: + # check local typedefs + pmodule = node.i_module + typedef = statements.search_typedef(t, name) + else: + # this is a prefixed name, check the imported modules + err = [] + pmodule = statements.prefix_to_module(t.i_module,prefix,t.pos,err) + if pmodule is None: + return + typedef = statements.search_typedef(pmodule, name) + + if typedef is None: + print("Typedef ", name, " is not found, make sure all dependent modules are present") + sys.exit(2) + t=typedef.search_one('type') + + return resolveType(t, t.arg) + + +class Abort(Exception): + """used to abort an iteration""" + pass + +def isUriKeyInPayload(stmt, keyNodesList): + result = False # atleast one key is present + + def checkFunc(node): + result = "continue" + if node in keyNodesList: + result = "stop" + return result + + def _iterate(stmt): + res = "continue" + if stmt.keyword == "leaf" or \ + stmt.keyword == "leaf-list": + res = checkFunc(stmt) + if res == 'stop': + raise Abort + else: + # default is to recurse + if hasattr(stmt, 'i_children'): + for s in stmt.i_children: + _iterate(s) + + try: + _iterate(stmt) + except Abort: + result = True + + return result 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/.gitignore b/tools/swagger_codegen/.gitignore new file mode 100644 index 0000000000..31be659360 --- /dev/null +++ b/tools/swagger_codegen/.gitignore @@ -0,0 +1 @@ +swagger-codegen-*.jar diff --git a/tools/swagger_codegen/go-server/src/swagger/routes.go b/tools/swagger_codegen/go-server/src/swagger/routes.go new file mode 100644 index 0000000000..ce305b72aa --- /dev/null +++ b/tools/swagger_codegen/go-server/src/swagger/routes.go @@ -0,0 +1,24 @@ +//////////////////////////////////////////////////////////////////////////////// +// // +// 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 + +// Load function logs swagger generated routes into REST server. +func Load() { +} diff --git a/tools/swagger_codegen/go-server/templates-nonyang/README.mustache b/tools/swagger_codegen/go-server/templates-nonyang/README.mustache new file mode 100644 index 0000000000..89019f8e21 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/README.mustache @@ -0,0 +1,30 @@ +# Go API Server for {{packageName}} + +{{#appDescription}} +{{{appDescription}}} +{{/appDescription}} + +## Overview +This server was generated by the [swagger-codegen] +(https://github.com/swagger-api/swagger-codegen) project. +By using the [OpenAPI-Spec](https://github.com/OAI/OpenAPI-Specification) from a remote server, you can easily generate a server stub. +- + +To see how to make this your own, look here: + +[README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md) + +- API version: {{appVersion}}{{^hideGenerationTimestamp}} +- Build date: {{generatedDate}}{{/hideGenerationTimestamp}} +{{#infoUrl}} +For more information, please visit [{{{infoUrl}}}]({{{infoUrl}}}) +{{/infoUrl}} + + +### Running the server +To run the server, follow these simple steps: + +``` +go run main.go +``` + diff --git a/tools/swagger_codegen/go-server/templates-nonyang/controller-api.mustache b/tools/swagger_codegen/go-server/templates-nonyang/controller-api.mustache new file mode 100644 index 0000000000..c1bbec0e8c --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/controller-api.mustache @@ -0,0 +1,24 @@ +{{>partial_header}} +package {{packageName}} + +{{#operations}} +import ( + "net/http" + + "rest/server" +){{#operation}} + +func {{nickname}}(w http.ResponseWriter, r *http.Request) { + rc, r := server.GetContext(r) + rc.Name = "{{operationId}}" + {{#consumes}} + rc.Consumes.Add("{{mediaType}}") + {{/consumes}} + {{#produces}} + rc.Produces.Add("{{mediaType}}") + {{/produces}} + {{#bodyParam.required}} + rc.Model = &{{bodyParam.dataType}}{} + {{/bodyParam.required}} + server.Process(w, r) +}{{/operation}}{{/operations}} diff --git a/tools/swagger_codegen/go-server/templates-nonyang/logger.mustache b/tools/swagger_codegen/go-server/templates-nonyang/logger.mustache new file mode 100644 index 0000000000..aa0d894d83 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/logger.mustache @@ -0,0 +1,24 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "log" + "net/http" + "time" +) + +func Logger(inner http.Handler, name string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + + inner.ServeHTTP(w, r) + + log.Printf( + "%s %s %s %s", + r.Method, + r.RequestURI, + name, + time.Since(start), + ) + }) +} diff --git a/tools/swagger_codegen/go-server/templates-nonyang/main.mustache b/tools/swagger_codegen/go-server/templates-nonyang/main.mustache new file mode 100644 index 0000000000..5a1de41602 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/main.mustache @@ -0,0 +1,33 @@ +{{>partial_header}} +package main + +import ( + "flag" + "fmt" + "log" + "net/http" + + // WARNING! + // Change this to a fully-qualified import path + // once you place this file into your project. + // For example, + // + // sw "github.com/myname/myrepo/{{apiPath}}" + // + sw "./{{apiPath}}" +) + +func main() { + var port int + + flag.IntVar(&port, "port", {{serverPort}}, "Listen port") + flag.Parse() + + address := fmt.Sprintf(":%d", port) + + log.Printf("Server started on %s", address) + + router := sw.NewRouter() + + log.Fatal(http.ListenAndServe(address, router)) +} diff --git a/tools/swagger_codegen/go-server/templates-nonyang/model.mustache b/tools/swagger_codegen/go-server/templates-nonyang/model.mustache new file mode 100644 index 0000000000..8754da0581 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/model.mustache @@ -0,0 +1,32 @@ +{{>partial_header}} +package {{packageName}} +{{#models}}{{#imports}} +import ({{/imports}}{{#imports}} + "{{import}}"{{/imports}}{{#imports}} +) +{{/imports}}{{#model}}{{#isEnum}}{{#description}}// {{{classname}}} : {{{description}}}{{/description}} +type {{{name}}} {{^format}}{{dataType}}{{/format}}{{#format}}{{{format}}}{{/format}} + +// List of {{{name}}} +const ( + {{#allowableValues}} + {{#enumVars}} + {{name}} {{{classname}}} = "{{{value}}}" + {{/enumVars}} + {{/allowableValues}} +){{/isEnum}}{{^isEnum}}{{#description}} +// {{{description}}}{{/description}} +type {{classname}} struct { +{{#requiredVars}} + {{name}} {{^isEnum}}{{^isPrimitiveType}}{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{/isPrimitiveType}}{{/isEnum}}{{{datatype}}} `validate:"required" json:"{{baseName}}{{^required}},omitempty{{/required}}"` +{{/requiredVars}} +{{#vars}}{{#description}} + // {{{description}}}{{/description}} +{{^required}}{{#isContainer}} + {{name}} {{^isEnum}}{{^isPrimitiveType}}{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{/isPrimitiveType}}{{/isEnum}}{{{datatype}}} `validate:"dive" json:"{{baseName}}{{^required}},omitempty{{/required}}"` +{{/isContainer}}{{/required}} +{{^required}}{{^isContainer}} + {{name}} {{^isEnum}}{{^isPrimitiveType}}{{^isContainer}}{{^isDateTime}}*{{/isDateTime}}{{/isContainer}}{{/isPrimitiveType}}{{/isEnum}}{{{datatype}}} `json:"{{baseName}}{{^required}},omitempty{{/required}}"` +{{/isContainer}}{{/required}} +{{/vars}} +}{{/isEnum}}{{/model}}{{/models}} diff --git a/tools/swagger_codegen/go-server/templates-nonyang/partial_header.mustache b/tools/swagger_codegen/go-server/templates-nonyang/partial_header.mustache new file mode 100644 index 0000000000..d24dfec369 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/partial_header.mustache @@ -0,0 +1,17 @@ +/* + {{#appName}} + * {{{appName}}} + * + {{/appName}} + {{#appDescription}} + * {{{appDescription}}} + * + {{/appDescription}} + {{#version}} + * API version: {{{version}}} + {{/version}} + {{#infoEmail}} + * Contact: {{{infoEmail}}} + {{/infoEmail}} + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ diff --git a/tools/swagger_codegen/go-server/templates-nonyang/routers.mustache b/tools/swagger_codegen/go-server/templates-nonyang/routers.mustache new file mode 100644 index 0000000000..cb0dc6e503 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/routers.mustache @@ -0,0 +1,17 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "rest/server" +) + +func init() { + {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} + server.AddRoute( + "{{operationId}}", + "{{httpMethod}}", + "{{{basePathWithoutHost}}}{{{path}}}", + {{operationId}}, + ) + {{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} diff --git a/tools/swagger_codegen/go-server/templates-nonyang/swagger.mustache b/tools/swagger_codegen/go-server/templates-nonyang/swagger.mustache new file mode 100644 index 0000000000..51560926bb --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-nonyang/swagger.mustache @@ -0,0 +1 @@ +{{{swagger-yaml}}} \ No newline at end of file diff --git a/tools/swagger_codegen/go-server/templates-yang/controller-api.mustache b/tools/swagger_codegen/go-server/templates-yang/controller-api.mustache new file mode 100644 index 0000000000..15b5c77a3e --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-yang/controller-api.mustache @@ -0,0 +1,26 @@ +{{>partial_header}} +package {{packageName}} + +{{#operations}} +import ( + "net/http" + + "rest/server" +){{#operation}} + +func {{nickname}}(w http.ResponseWriter, r *http.Request) { + rc, r := server.GetContext(r) + rc.Name = "{{operationId}}" + {{#consumes}} + rc.Consumes.Add("{{mediaType}}") + {{/consumes}} + {{#produces}} + rc.Produces.Add("{{mediaType}}") + {{/produces}} + {{#vendorExtensions}} + {{#x-params}} + rc.PMap = server.NameMap{ {{#varMapping}}"{{uriName}}":"{{yangName}}", {{/varMapping}} } + {{/x-params}} + {{/vendorExtensions}} + server.Process(w, r) +}{{/operation}}{{/operations}} diff --git a/tools/swagger_codegen/go-server/templates-yang/routers.mustache b/tools/swagger_codegen/go-server/templates-yang/routers.mustache new file mode 100644 index 0000000000..cb0dc6e503 --- /dev/null +++ b/tools/swagger_codegen/go-server/templates-yang/routers.mustache @@ -0,0 +1,17 @@ +{{>partial_header}} +package {{packageName}} + +import ( + "rest/server" +) + +func init() { + {{#apiInfo}}{{#apis}}{{#operations}}{{#operation}} + server.AddRoute( + "{{operationId}}", + "{{httpMethod}}", + "{{{basePathWithoutHost}}}{{{path}}}", + {{operationId}}, + ) + {{/operation}}{{/operations}}{{/apis}}{{/apiInfo}} +} diff --git a/tools/swagger_codegen/py-client/templates/configuration.mustache b/tools/swagger_codegen/py-client/templates/configuration.mustache new file mode 100644 index 0000000000..84cc3a2ed5 --- /dev/null +++ b/tools/swagger_codegen/py-client/templates/configuration.mustache @@ -0,0 +1,259 @@ +# coding: utf-8 + +{{>partial_header}} + +from __future__ import absolute_import + +import copy +import logging +import multiprocessing +import sys +import os +import urllib3 + +import six +from six.moves import http_client as httplib + + +class Configuration(object): + """NOTE: This class is auto generated by the swagger code generator program. + + Ref: https://github.com/swagger-api/swagger-codegen + Do not edit the class manually. + """ + + _default = None + + def __init__(self): + """Constructor""" + if self._default: + for key in self._default.__dict__.keys(): + self.__dict__[key] = copy.copy(self._default.__dict__[key]) + return + + # Default Base url + self.host = os.getenv("REST_API_ROOT", "{{{basePath}}}") + + # Temp file folder for downloading files + self.temp_folder_path = None + + # Authentication Settings + # dict to store API key(s) + self.api_key = {} + # dict to store API prefix (e.g. Bearer) + self.api_key_prefix = {} + # Username for HTTP basic authentication + self.username = "" + # Password for HTTP basic authentication + self.password = "" +{{#authMethods}}{{#isOAuth}} + # access token for OAuth + self.access_token = "" +{{/isOAuth}}{{/authMethods}} + # Logging Settings + self.logger = {} + self.logger["package_logger"] = logging.getLogger("{{packageName}}") + self.logger["urllib3_logger"] = logging.getLogger("urllib3") + # Log format + self.logger_format = '%(asctime)s %(levelname)s %(message)s' + # Log stream handler + self.logger_stream_handler = None + # Log file handler + self.logger_file_handler = None + # Debug file location + self.logger_file = None + # Debug switch + self.debug = False + + # SSL/TLS verification + # Set this to false to skip verifying SSL certificate when calling API + # from https server. + self.verify_ssl = True + # Set this to customize the certificate file to verify the peer. + self.ssl_ca_cert = None + # client certificate file + self.cert_file = None + # client key file + self.key_file = None + # Set this to True/False to enable/disable SSL hostname verification. + self.assert_hostname = None + + # urllib3 connection pool's maximum number of connections saved + # per pool. urllib3 uses 1 connection as default value, but this is + # not the best value when you are making a lot of possibly parallel + # requests to the same host, which is often the case here. + # cpu_count * 5 is used as default value to increase performance. + self.connection_pool_maxsize = multiprocessing.cpu_count() * 5 + + # Proxy URL + self.proxy = None + # Safe chars for path_param + self.safe_chars_for_path_param = '' + + @classmethod + def set_default(cls, default): + cls._default = default + + @property + def logger_file(self): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + return self.__logger_file + + @logger_file.setter + def logger_file(self, value): + """The logger file. + + If the logger_file is None, then add stream handler and remove file + handler. Otherwise, add file handler and remove stream handler. + + :param value: The logger_file path. + :type: str + """ + self.__logger_file = value + if self.__logger_file: + # If set logging file, + # then add file handler and remove stream handler. + self.logger_file_handler = logging.FileHandler(self.__logger_file) + self.logger_file_handler.setFormatter(self.logger_formatter) + for _, logger in six.iteritems(self.logger): + logger.addHandler(self.logger_file_handler) + if self.logger_stream_handler: + logger.removeHandler(self.logger_stream_handler) + else: + # If not set logging file, + # then add stream handler and remove file handler. + self.logger_stream_handler = logging.StreamHandler() + self.logger_stream_handler.setFormatter(self.logger_formatter) + for _, logger in six.iteritems(self.logger): + logger.addHandler(self.logger_stream_handler) + if self.logger_file_handler: + logger.removeHandler(self.logger_file_handler) + + @property + def debug(self): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + return self.__debug + + @debug.setter + def debug(self, value): + """Debug status + + :param value: The debug status, True or False. + :type: bool + """ + self.__debug = value + if self.__debug: + # if debug status is True, turn on debug logging + for _, logger in six.iteritems(self.logger): + logger.setLevel(logging.DEBUG) + # turn on httplib debug + httplib.HTTPConnection.debuglevel = 1 + else: + # if debug status is False, turn off debug logging, + # setting log level to default `logging.WARNING` + for _, logger in six.iteritems(self.logger): + logger.setLevel(logging.WARNING) + # turn off httplib debug + httplib.HTTPConnection.debuglevel = 0 + + @property + def logger_format(self): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + return self.__logger_format + + @logger_format.setter + def logger_format(self, value): + """The logger format. + + The logger_formatter will be updated when sets logger_format. + + :param value: The format string. + :type: str + """ + self.__logger_format = value + self.logger_formatter = logging.Formatter(self.__logger_format) + + def get_api_key_with_prefix(self, identifier): + """Gets API key (with prefix if set). + + :param identifier: The identifier of apiKey. + :return: The token for api key authentication. + """ + if (self.api_key.get(identifier) and + self.api_key_prefix.get(identifier)): + return self.api_key_prefix[identifier] + ' ' + self.api_key[identifier] # noqa: E501 + elif self.api_key.get(identifier): + return self.api_key[identifier] + + def get_basic_auth_token(self): + """Gets HTTP basic authentication header (string). + + :return: The token for basic HTTP authentication. + """ + return urllib3.util.make_headers( + basic_auth=self.username + ':' + self.password + ).get('authorization') + + def auth_settings(self): + """Gets Auth Settings dict for api client. + + :return: The Auth Settings information dict. + """ + return { +{{#authMethods}} +{{#isApiKey}} + '{{name}}': + { + 'type': 'api_key', + 'in': {{#isKeyInHeader}}'header'{{/isKeyInHeader}}{{#isKeyInQuery}}'query'{{/isKeyInQuery}}, + 'key': '{{keyParamName}}', + 'value': self.get_api_key_with_prefix('{{keyParamName}}') + }, +{{/isApiKey}} +{{#isBasic}} + '{{name}}': + { + 'type': 'basic', + 'in': 'header', + 'key': 'Authorization', + 'value': self.get_basic_auth_token() + }, +{{/isBasic}}{{#isOAuth}} + '{{name}}': + { + 'type': 'oauth2', + 'in': 'header', + 'key': 'Authorization', + 'value': 'Bearer ' + self.access_token + }, +{{/isOAuth}}{{/authMethods}} + } + + def to_debug_report(self): + """Gets the essential information for debugging. + + :return: The report for debugging. + """ + return "Python SDK Debug Report:\n"\ + "OS: {env}\n"\ + "Python Version: {pyversion}\n"\ + "Version of the API: {{version}}\n"\ + "SDK Package Version: {{packageVersion}}".\ + format(env=sys.platform, pyversion=sys.version) diff --git a/tools/swagger_codegen/ui-dist/favicon-16x16.png b/tools/swagger_codegen/ui-dist/favicon-16x16.png new file mode 100644 index 0000000000..8b194e617a Binary files /dev/null and b/tools/swagger_codegen/ui-dist/favicon-16x16.png differ diff --git a/tools/swagger_codegen/ui-dist/favicon-32x32.png b/tools/swagger_codegen/ui-dist/favicon-32x32.png new file mode 100644 index 0000000000..249737fe44 Binary files /dev/null and b/tools/swagger_codegen/ui-dist/favicon-32x32.png differ diff --git a/tools/swagger_codegen/ui-dist/index.html_notused b/tools/swagger_codegen/ui-dist/index.html_notused new file mode 100644 index 0000000000..f2e69e4f8b --- /dev/null +++ b/tools/swagger_codegen/ui-dist/index.html_notused @@ -0,0 +1,60 @@ + + + + + + Swagger UI + + + + + + + +
+ + + + + + diff --git a/tools/swagger_codegen/ui-dist/oauth2-redirect.html b/tools/swagger_codegen/ui-dist/oauth2-redirect.html new file mode 100644 index 0000000000..fb68399d26 --- /dev/null +++ b/tools/swagger_codegen/ui-dist/oauth2-redirect.html @@ -0,0 +1,67 @@ + + + + + + diff --git a/tools/swagger_codegen/ui-dist/swagger-ui-bundle.js b/tools/swagger_codegen/ui-dist/swagger-ui-bundle.js new file mode 100644 index 0000000000..55e2f50c0a --- /dev/null +++ b/tools/swagger_codegen/ui-dist/swagger-ui-bundle.js @@ -0,0 +1,93 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/dist",n(n.s=446)}([function(e,t,n){"use strict";e.exports=n(75)},function(e,t,n){e.exports=n(854)()},function(e,t,n){"use strict";t.__esModule=!0,t.default=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}},function(e,t,n){"use strict";t.__esModule=!0;var r,o=n(263),i=(r=o)&&r.__esModule?r:{default:r};t.default=function(){function e(e,t){for(var n=0;n>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?C(e)+t:t}function A(){return!0}function O(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function P(e,t){return M(e,t,0)}function T(e,t){return M(e,t,t)}function M(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var I=0,j=1,N=2,R="function"==typeof Symbol&&Symbol.iterator,D="@@iterator",L=R||D;function U(e){this.next=e}function q(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function F(){return{value:void 0,done:!0}}function z(e){return!!H(e)}function B(e){return e&&"function"==typeof e.next}function V(e){var t=H(e);return t&&t.call(e)}function H(e){var t=e&&(R&&e[R]||e[D]);if("function"==typeof t)return t}function W(e){return e&&"number"==typeof e.length}function J(e){return null===e||void 0===e?ie():a(e)?e.toSeq():function(e){var t=se(e)||"object"==typeof e&&new te(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}(e)}function Y(e){return null===e||void 0===e?ie().toKeyedSeq():a(e)?u(e)?e.toSeq():e.fromEntrySeq():ae(e)}function K(e){return null===e||void 0===e?ie():a(e)?u(e)?e.entrySeq():e.toIndexedSeq():ue(e)}function G(e){return(null===e||void 0===e?ie():a(e)?u(e)?e.entrySeq():e:ue(e)).toSetSeq()}U.prototype.toString=function(){return"[Iterator]"},U.KEYS=I,U.VALUES=j,U.ENTRIES=N,U.prototype.inspect=U.prototype.toSource=function(){return this.toString()},U.prototype[L]=function(){return this},t(J,n),J.of=function(){return J(arguments)},J.prototype.toSeq=function(){return this},J.prototype.toString=function(){return this.__toString("Seq {","}")},J.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},J.prototype.__iterate=function(e,t){return le(this,e,t,!0)},J.prototype.__iterator=function(e,t){return ce(this,e,t,!0)},t(Y,J),Y.prototype.toKeyedSeq=function(){return this},t(K,J),K.of=function(){return K(arguments)},K.prototype.toIndexedSeq=function(){return this},K.prototype.toString=function(){return this.__toString("Seq [","]")},K.prototype.__iterate=function(e,t){return le(this,e,t,!1)},K.prototype.__iterator=function(e,t){return ce(this,e,t,!1)},t(G,J),G.of=function(){return G(arguments)},G.prototype.toSetSeq=function(){return this},J.isSeq=oe,J.Keyed=Y,J.Set=G,J.Indexed=K;var $,Z,X,Q="@@__IMMUTABLE_SEQ__@@";function ee(e){this._array=e,this.size=e.length}function te(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function ne(e){this._iterable=e,this.size=e.length||e.size}function re(e){this._iterator=e,this._iteratorCache=[]}function oe(e){return!(!e||!e[Q])}function ie(){return $||($=new ee([]))}function ae(e){var t=Array.isArray(e)?new ee(e).fromEntrySeq():B(e)?new re(e).fromEntrySeq():z(e)?new ne(e).fromEntrySeq():"object"==typeof e?new te(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function ue(e){var t=se(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function se(e){return W(e)?new ee(e):B(e)?new re(e):z(e)?new ne(e):void 0}function le(e,t,n,r){var o=e._cache;if(o){for(var i=o.length-1,a=0;a<=i;a++){var u=o[n?i-a:a];if(!1===t(u[1],r?u[0]:a,e))return a+1}return a}return e.__iterateUncached(t,n)}function ce(e,t,n,r){var o=e._cache;if(o){var i=o.length-1,a=0;return new U(function(){var e=o[n?i-a:a];return a++>i?{value:void 0,done:!0}:q(t,r?e[0]:a-1,e[1])})}return e.__iteratorUncached(t,n)}function fe(e,t){return t?function e(t,n,r,o){if(Array.isArray(n))return t.call(o,r,K(n).map(function(r,o){return e(t,r,o,n)}));if(de(n))return t.call(o,r,Y(n).map(function(r,o){return e(t,r,o,n)}));return n}(t,e,"",{"":e}):pe(e)}function pe(e){return Array.isArray(e)?K(e).map(pe).toList():de(e)?Y(e).map(pe).toMap():e}function de(e){return e&&(e.constructor===Object||void 0===e.constructor)}function he(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function ve(e,t){if(e===t)return!0;if(!a(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||u(e)!==u(t)||s(e)!==s(t)||c(e)!==c(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!l(e);if(c(e)){var r=e.entries();return t.every(function(e,t){var o=r.next().value;return o&&he(o[1],e)&&(n||he(o[0],t))})&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var i=e;e=t,t=i}var f=!0,p=t.__iterate(function(t,r){if(n?!e.has(t):o?!he(t,e.get(r,y)):!he(e.get(r,y),t))return f=!1,!1});return f&&e.size===p}function me(e,t){if(!(this instanceof me))return new me(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(Z)return Z;Z=this}}function ge(e,t){if(!e)throw new Error(t)}function ye(e,t,n){if(!(this instanceof ye))return new ye(e,t,n);if(ge(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?{value:void 0,done:!0}:q(e,o,n[t?r-o++:o++])})},t(te,Y),te.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},te.prototype.has=function(e){return this._object.hasOwnProperty(e)},te.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,i=0;i<=o;i++){var a=r[t?o-i:i];if(!1===e(n[a],a,this))return i+1}return i},te.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,i=0;return new U(function(){var a=r[t?o-i:i];return i++>o?{value:void 0,done:!0}:q(e,a,n[a])})},te.prototype[h]=!0,t(ne,K),ne.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=V(this._iterable),r=0;if(B(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},ne.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=V(this._iterable);if(!B(n))return new U(F);var r=0;return new U(function(){var t=n.next();return t.done?t:q(e,r++,t.value)})},t(re,K),re.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,i=0;i=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return q(e,o,r[o++])})},t(me,K),me.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},me.prototype.get=function(e,t){return this.has(e)?this._value:t},me.prototype.includes=function(e){return he(this._value,e)},me.prototype.slice=function(e,t){var n=this.size;return O(e,t,n)?this:new me(this._value,T(t,n)-P(e,n))},me.prototype.reverse=function(){return this},me.prototype.indexOf=function(e){return he(this._value,e)?0:-1},me.prototype.lastIndexOf=function(e){return he(this._value,e)?this.size:-1},me.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?{value:void 0,done:!0}:q(e,i++,a)})},ye.prototype.equals=function(e){return e instanceof ye?this._start===e._start&&this._end===e._end&&this._step===e._step:ve(this,e)},t(be,n),t(_e,be),t(we,be),t(Ee,be),be.Keyed=_e,be.Indexed=we,be.Set=Ee;var xe="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function Se(e){return e>>>1&1073741824|3221225471&e}function Ce(e){if(!1===e||null===e||void 0===e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null===e||void 0===e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return Se(n)}if("string"===t)return e.length>je?function(e){var t=De[e];void 0===t&&(t=ke(e),Re===Ne&&(Re=0,De={}),Re++,De[e]=t);return t}(e):ke(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return function(e){var t;if(Te&&void 0!==(t=Pe.get(e)))return t;if(void 0!==(t=e[Ie]))return t;if(!Oe){if(void 0!==(t=e.propertyIsEnumerable&&e.propertyIsEnumerable[Ie]))return t;if(void 0!==(t=function(e){if(e&&e.nodeType>0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}(e)))return t}t=++Me,1073741824&Me&&(Me=0);if(Te)Pe.set(e,t);else{if(void 0!==Ae&&!1===Ae(e))throw new Error("Non-extensible objects are not allowed as keys.");if(Oe)Object.defineProperty(e,Ie,{enumerable:!1,configurable:!1,writable:!1,value:t});else if(void 0!==e.propertyIsEnumerable&&e.propertyIsEnumerable===e.constructor.prototype.propertyIsEnumerable)e.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},e.propertyIsEnumerable[Ie]=t;else{if(void 0===e.nodeType)throw new Error("Unable to set a non-enumerable property on object.");e[Ie]=t}}return t}(e);if("function"==typeof e.toString)return ke(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function ke(e){for(var t=0,n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}})},Ue.prototype.toString=function(){return this.__toString("Map {","}")},Ue.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Ue.prototype.set=function(e,t){return Qe(this,e,t)},Ue.prototype.setIn=function(e,t){return this.updateIn(e,y,function(){return t})},Ue.prototype.remove=function(e){return Qe(this,e,y)},Ue.prototype.deleteIn=function(e){return this.updateIn(e,function(){return y})},Ue.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},Ue.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=function e(t,n,r,o){var i=t===y;var a=n.next();if(a.done){var u=i?r:t,s=o(u);return s===u?t:s}ge(i||t&&t.set,"invalid keyPath");var l=a.value;var c=i?y:t.get(l,y);var f=e(c,n,r,o);return f===c?t:f===y?t.remove(l):(i?Xe():t).set(l,f)}(this,nn(e),t,n);return r===y?void 0:r},Ue.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):Xe()},Ue.prototype.merge=function(){return rt(this,void 0,arguments)},Ue.prototype.mergeWith=function(t){return rt(this,t,e.call(arguments,1))},Ue.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,Xe(),function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]})},Ue.prototype.mergeDeep=function(){return rt(this,ot,arguments)},Ue.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return rt(this,it(t),n)},Ue.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,Xe(),function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]})},Ue.prototype.sort=function(e){return Pt(Wt(this,e))},Ue.prototype.sortBy=function(e,t){return Pt(Wt(this,t,e))},Ue.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Ue.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new x)},Ue.prototype.asImmutable=function(){return this.__ensureOwner()},Ue.prototype.wasAltered=function(){return this.__altered},Ue.prototype.__iterator=function(e,t){return new Ke(this,e,t)},Ue.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate(function(t){return r++,e(t[1],t[0],n)},t),r},Ue.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Ze(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Ue.isMap=qe;var Fe,ze="@@__IMMUTABLE_MAP__@@",Be=Ue.prototype;function Ve(e,t){this.ownerID=e,this.entries=t}function He(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function We(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Je(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Ye(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function Ke(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&$e(e._root)}function Ge(e,t){return q(e,t[0],t[1])}function $e(e,t){return{node:e,index:0,__prev:t}}function Ze(e,t,n,r){var o=Object.create(Be);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function Xe(){return Fe||(Fe=Ze(0))}function Qe(e,t,n){var r,o;if(e._root){var i=w(b),a=w(_);if(r=et(e._root,e.__ownerID,0,void 0,t,n,i,a),!a.value)return e;o=e.size+(i.value?n===y?-1:1:0)}else{if(n===y)return e;o=1,r=new Ve(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?Ze(o,r):Xe()}function et(e,t,n,r,o,i,a,u){return e?e.update(t,n,r,o,i,a,u):i===y?e:(E(u),E(a),new Ye(t,r,[o,i]))}function tt(e){return e.constructor===Ye||e.constructor===Je}function nt(e,t,n,r,o){if(e.keyHash===r)return new Je(t,r,[e.entry,o]);var i,a=(0===n?e.keyHash:e.keyHash>>>n)&g,u=(0===n?r:r>>>n)&g;return new He(t,1<>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function st(e,t,n,r){var o=r?e:S(e);return o[t]=n,o}Be[ze]=!0,Be.delete=Be.remove,Be.removeIn=Be.deleteIn,Ve.prototype.get=function(e,t,n,r){for(var o=this.entries,i=0,a=o.length;i=lt)return function(e,t,n,r){e||(e=new x);for(var o=new Ye(e,Ce(n),[n,r]),i=0;i>>e)&g),i=this.bitmap;return 0==(i&o)?r:this.nodes[ut(i&o-1)].get(e+v,t,n,r)},He.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=Ce(r));var u=(0===t?n:n>>>t)&g,s=1<=ct)return function(e,t,n,r,o){for(var i=0,a=new Array(m),u=0;0!==n;u++,n>>>=1)a[u]=1&n?t[i++]:void 0;return a[r]=o,new We(e,i+1,a)}(e,p,l,u,h);if(c&&!h&&2===p.length&&tt(p[1^f]))return p[1^f];if(c&&h&&1===p.length&&tt(h))return h;var b=e&&e===this.ownerID,_=c?h?l:l^s:l|s,w=c?h?st(p,f,h,b):function(e,t,n){var r=e.length-1;if(n&&t===r)return e.pop(),e;for(var o=new Array(r),i=0,a=0;a>>e)&g,i=this.nodes[o];return i?i.get(e+v,t,n,r):r},We.prototype.update=function(e,t,n,r,o,i,a){void 0===n&&(n=Ce(r));var u=(0===t?n:n>>>t)&g,s=o===y,l=this.nodes,c=l[u];if(s&&!c)return this;var f=et(c,e,t+v,n,r,o,i,a);if(f===c)return this;var p=this.count;if(c){if(!f&&--p0&&r=0&&e=e.size||t<0)return e.withMutations(function(e){t<0?kt(e,t).set(0,n):kt(e,0,t+1).set(t,n)});t+=e._origin;var r=e._tail,o=e._root,i=w(_);t>=Ot(e._capacity)?r=xt(r,e.__ownerID,0,t,n,i):o=xt(o,e.__ownerID,e._level,t,n,i);if(!i.value)return e;if(e.__ownerID)return e._root=o,e._tail=r,e.__hash=void 0,e.__altered=!0,e;return wt(e._origin,e._capacity,e._level,o,r)}(this,e,t)},pt.prototype.remove=function(e){return this.has(e)?0===e?this.shift():e===this.size-1?this.pop():this.splice(e,1):this},pt.prototype.insert=function(e,t){return this.splice(e,0,t)},pt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=v,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):Et()},pt.prototype.push=function(){var e=arguments,t=this.size;return this.withMutations(function(n){kt(n,0,t+e.length);for(var r=0;r>>t&g;if(r>=this.array.length)return new mt([],e);var o,i=0===r;if(t>0){var a=this.array[r];if((o=a&&a.removeBefore(e,t-v,n))===a&&i)return this}if(i&&!o)return this;var u=St(this,e);if(!i)for(var s=0;s>>t&g;if(o>=this.array.length)return this;if(t>0){var i=this.array[o];if((r=i&&i.removeAfter(e,t-v,n))===i&&o===this.array.length-1)return this}var a=St(this,e);return a.array.splice(o+1),r&&(a.array[o]=r),a};var gt,yt,bt={};function _t(e,t){var n=e._origin,r=e._capacity,o=Ot(r),i=e._tail;return a(e._root,e._level,0);function a(e,u,s){return 0===u?function(e,a){var u=a===o?i&&i.array:e&&e.array,s=a>n?0:n-a,l=r-a;l>m&&(l=m);return function(){if(s===l)return bt;var e=t?--l:s++;return u&&u[e]}}(e,s):function(e,o,i){var u,s=e&&e.array,l=i>n?0:n-i>>o,c=1+(r-i>>o);c>m&&(c=m);return function(){for(;;){if(u){var e=u();if(e!==bt)return e;u=null}if(l===c)return bt;var n=t?--c:l++;u=a(s&&s[n],o-v,i+(n<>>n&g,s=e&&u0){var l=e&&e.array[u],c=xt(l,t,n-v,r,o,i);return c===l?e:((a=St(e,t)).array[u]=c,a)}return s&&e.array[u]===o?e:(E(i),a=St(e,t),void 0===o&&u===a.array.length-1?a.array.pop():a.array[u]=o,a)}function St(e,t){return t&&e&&t===e.ownerID?e:new mt(e?e.array.slice():[],t)}function Ct(e,t){if(t>=Ot(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&g],r-=v;return n}}function kt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new x,o=e._origin,i=e._capacity,a=o+t,u=void 0===n?i:n<0?i+n:o+n;if(a===o&&u===i)return e;if(a>=u)return e.clear();for(var s=e._level,l=e._root,c=0;a+c<0;)l=new mt(l&&l.array.length?[void 0,l]:[],r),c+=1<<(s+=v);c&&(a+=c,o+=c,u+=c,i+=c);for(var f=Ot(i),p=Ot(u);p>=1<f?new mt([],r):d;if(d&&p>f&&av;y-=v){var b=f>>>y&g;m=m.array[b]=St(m.array[b],r)}m.array[f>>>v&g]=d}if(u=p)a-=p,u-=p,s=v,l=null,h=h&&h.removeBefore(r,0,a);else if(a>o||p>>s&g;if(_!==p>>>s&g)break;_&&(c+=(1<o&&(l=l.removeBefore(r,s,a-c)),l&&pi&&(i=l.size),a(s)||(l=l.map(function(e){return fe(e)})),r.push(l)}return i>e.size&&(e=e.setSize(i)),at(e,t,r)}function Ot(e){return e>>v<=m&&a.size>=2*i.size?(r=(o=a.filter(function(e,t){return void 0!==e&&u!==t})).toKeyedSeq().map(function(e){return e[0]}).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=i.remove(t),o=u===a.size-1?a.pop():a.set(u,void 0))}else if(s){if(n===a.get(u)[1])return e;r=i,o=a.set(u,[t,n])}else r=i.set(t,a.size),o=a.set(a.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Mt(r,o)}function Nt(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function Rt(e){this._iter=e,this.size=e.size}function Dt(e){this._iter=e,this.size=e.size}function Lt(e){this._iter=e,this.size=e.size}function Ut(e){var t=Qt(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=en,t.__iterateUncached=function(t,n){var r=this;return e.__iterate(function(e,n){return!1!==t(n,e,r)},n)},t.__iteratorUncached=function(t,n){if(t===N){var r=e.__iterator(t,n);return new U(function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e})}return e.__iterator(t===j?I:j,n)},t}function qt(e,t,n){var r=Qt(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,o){var i=e.get(r,y);return i===y?o:t.call(n,i,r,e)},r.__iterateUncached=function(r,o){var i=this;return e.__iterate(function(e,o,a){return!1!==r(t.call(n,e,o,a),o,i)},o)},r.__iteratorUncached=function(r,o){var i=e.__iterator(N,o);return new U(function(){var o=i.next();if(o.done)return o;var a=o.value,u=a[0];return q(r,u,t.call(n,a[1],u,e),o)})},r}function Ft(e,t){var n=Qt(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=Ut(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=en,n.__iterate=function(t,n){var r=this;return e.__iterate(function(e,n){return t(e,n,r)},!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function zt(e,t,n,r){var o=Qt(e);return r&&(o.has=function(r){var o=e.get(r,y);return o!==y&&!!t.call(n,o,r,e)},o.get=function(r,o){var i=e.get(r,y);return i!==y&&t.call(n,i,r,e)?i:o}),o.__iterateUncached=function(o,i){var a=this,u=0;return e.__iterate(function(e,i,s){if(t.call(n,e,i,s))return u++,o(e,r?i:u-1,a)},i),u},o.__iteratorUncached=function(o,i){var a=e.__iterator(N,i),u=0;return new U(function(){for(;;){var i=a.next();if(i.done)return i;var s=i.value,l=s[0],c=s[1];if(t.call(n,c,l,e))return q(o,r?l:u++,c,i)}})},o}function Bt(e,t,n,r){var o=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=o:n|=0),O(t,n,o))return e;var i=P(t,o),a=T(n,o);if(i!=i||a!=a)return Bt(e.toSeq().cacheResult(),t,n,r);var u,s=a-i;s==s&&(u=s<0?0:s);var l=Qt(e);return l.size=0===u?u:e.size&&u||void 0,!r&&oe(e)&&u>=0&&(l.get=function(t,n){return(t=k(this,t))>=0&&tu)return{value:void 0,done:!0};var e=o.next();return r||t===j?e:q(t,s-1,t===I?void 0:e.value[1],e)})},l}function Vt(e,t,n,r){var o=Qt(e);return o.__iterateUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterate(o,i);var u=!0,s=0;return e.__iterate(function(e,i,l){if(!u||!(u=t.call(n,e,i,l)))return s++,o(e,r?i:s-1,a)}),s},o.__iteratorUncached=function(o,i){var a=this;if(i)return this.cacheResult().__iterator(o,i);var u=e.__iterator(N,i),s=!0,l=0;return new U(function(){var e,i,c;do{if((e=u.next()).done)return r||o===j?e:q(o,l++,o===I?void 0:e.value[1],e);var f=e.value;i=f[0],c=f[1],s&&(s=t.call(n,c,i,a))}while(s);return o===N?e:q(o,i,c,e)})},o}function Ht(e,t,n){var r=Qt(e);return r.__iterateUncached=function(r,o){var i=0,u=!1;return function e(s,l){var c=this;s.__iterate(function(o,s){return(!t||l0}function Kt(e,t,r){var o=Qt(e);return o.size=new ee(r).map(function(e){return e.size}).min(),o.__iterate=function(e,t){for(var n,r=this.__iterator(j,t),o=0;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},o.__iteratorUncached=function(e,o){var i=r.map(function(e){return e=n(e),V(o?e.reverse():e)}),a=0,u=!1;return new U(function(){var n;return u||(n=i.map(function(e){return e.next()}),u=n.some(function(e){return e.done})),u?{value:void 0,done:!0}:q(e,a++,t.apply(null,n.map(function(e){return e.value})))})},o}function Gt(e,t){return oe(e)?t:e.constructor(t)}function $t(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function Zt(e){return Le(e.size),C(e)}function Xt(e){return u(e)?r:s(e)?o:i}function Qt(e){return Object.create((u(e)?Y:s(e)?K:G).prototype)}function en(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):J.prototype.cacheResult.call(this)}function tn(e,t){return e>t?1:e=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):An(e,t)},En.prototype.pushAll=function(e){if(0===(e=o(e)).size)return this;Le(e.size);var t=this.size,n=this._head;return e.reverse().forEach(function(e){t++,n={value:e,next:n}}),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):An(t,n)},En.prototype.pop=function(){return this.slice(1)},En.prototype.unshift=function(){return this.push.apply(this,arguments)},En.prototype.unshiftAll=function(e){return this.pushAll(e)},En.prototype.shift=function(){return this.pop.apply(this,arguments)},En.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):On()},En.prototype.slice=function(e,t){if(O(e,t,this.size))return this;var n=P(e,this.size);if(T(t,this.size)!==this.size)return we.prototype.slice.call(this,e,t);for(var r=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=r,this._head=o,this.__hash=void 0,this.__altered=!0,this):An(r,o)},En.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?An(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},En.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},En.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new U(function(){if(r){var t=r.value;return r=r.next,q(e,n++,t)}return{value:void 0,done:!0}})},En.isStack=xn;var Sn,Cn="@@__IMMUTABLE_STACK__@@",kn=En.prototype;function An(e,t,n,r){var o=Object.create(kn);return o.size=e,o._head=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function On(){return Sn||(Sn=An(0))}function Pn(e,t){var n=function(n){e.prototype[n]=t[n]};return Object.keys(t).forEach(n),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(n),e}kn[Cn]=!0,kn.withMutations=Be.withMutations,kn.asMutable=Be.asMutable,kn.asImmutable=Be.asImmutable,kn.wasAltered=Be.wasAltered,n.Iterator=U,Pn(n,{toArray:function(){Le(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate(function(t,n){e[n]=t}),e},toIndexedSeq:function(){return new Rt(this)},toJS:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJS?e.toJS():e}).__toJS()},toJSON:function(){return this.toSeq().map(function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e}).__toJS()},toKeyedSeq:function(){return new Nt(this,!0)},toMap:function(){return Ue(this.toKeyedSeq())},toObject:function(){Le(this.size);var e={};return this.__iterate(function(t,n){e[n]=t}),e},toOrderedMap:function(){return Pt(this.toKeyedSeq())},toOrderedSet:function(){return mn(u(this)?this.valueSeq():this)},toSet:function(){return sn(u(this)?this.valueSeq():this)},toSetSeq:function(){return new Dt(this)},toSeq:function(){return s(this)?this.toIndexedSeq():u(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return En(u(this)?this.valueSeq():this)},toList:function(){return pt(u(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){return Gt(this,function(e,t){var n=u(e),o=[e].concat(t).map(function(e){return a(e)?n&&(e=r(e)):e=n?ae(e):ue(Array.isArray(e)?e:[e]),e}).filter(function(e){return 0!==e.size});if(0===o.length)return e;if(1===o.length){var i=o[0];if(i===e||n&&u(i)||s(e)&&s(i))return i}var l=new ee(o);return n?l=l.toKeyedSeq():s(e)||(l=l.toSetSeq()),(l=l.flatten(!0)).size=o.reduce(function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}},0),l}(this,e.call(arguments,0)))},includes:function(e){return this.some(function(t){return he(t,e)})},entries:function(){return this.__iterator(N)},every:function(e,t){Le(this.size);var n=!0;return this.__iterate(function(r,o,i){if(!e.call(t,r,o,i))return n=!1,!1}),n},filter:function(e,t){return Gt(this,zt(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return Le(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){Le(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate(function(r){n?n=!1:t+=e,t+=null!==r&&void 0!==r?r.toString():""}),t},keys:function(){return this.__iterator(I)},map:function(e,t){return Gt(this,qt(this,e,t))},reduce:function(e,t,n){var r,o;return Le(this.size),arguments.length<2?o=!0:r=t,this.__iterate(function(t,i,a){o?(o=!1,r=t):r=e.call(n,r,t,i,a)}),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return Gt(this,Ft(this,!0))},slice:function(e,t){return Gt(this,Bt(this,e,t,!0))},some:function(e,t){return!this.every(Nn(e),t)},sort:function(e){return Gt(this,Wt(this,e))},values:function(){return this.__iterator(j)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(e,t){return C(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return function(e,t,n){var r=Ue().asMutable();return e.__iterate(function(o,i){r.update(t.call(n,o,i,e),0,function(e){return e+1})}),r.asImmutable()}(this,e,t)},equals:function(e){return ve(this,e)},entrySeq:function(){var e=this;if(e._cache)return new ee(e._cache);var t=e.toSeq().map(jn).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(Nn(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate(function(n,o,i){if(e.call(t,n,o,i))return r=[o,n],!1}),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(A)},flatMap:function(e,t){return Gt(this,function(e,t,n){var r=Xt(e);return e.toSeq().map(function(o,i){return r(t.call(n,o,i,e))}).flatten(!0)}(this,e,t))},flatten:function(e){return Gt(this,Ht(this,e,!0))},fromEntrySeq:function(){return new Lt(this)},get:function(e,t){return this.find(function(t,n){return he(n,e)},void 0,t)},getIn:function(e,t){for(var n,r=this,o=nn(e);!(n=o.next()).done;){var i=n.value;if((r=r&&r.get?r.get(i,y):y)===y)return t}return r},groupBy:function(e,t){return function(e,t,n){var r=u(e),o=(c(e)?Pt():Ue()).asMutable();e.__iterate(function(i,a){o.update(t.call(n,i,a,e),function(e){return(e=e||[]).push(r?[a,i]:i),e})});var i=Xt(e);return o.map(function(t){return Gt(e,i(t))})}(this,e,t)},has:function(e){return this.get(e,y)!==y},hasIn:function(e){return this.getIn(e,y)!==y},isSubset:function(e){return e="function"==typeof e.includes?e:n(e),this.every(function(t){return e.includes(t)})},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:n(e)).isSubset(this)},keyOf:function(e){return this.findKey(function(t){return he(t,e)})},keySeq:function(){return this.toSeq().map(In).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return Jt(this,e)},maxBy:function(e,t){return Jt(this,t,e)},min:function(e){return Jt(this,e?Rn(e):Un)},minBy:function(e,t){return Jt(this,t?Rn(t):Un,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return Gt(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return Gt(this,Vt(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(Nn(e),t)},sortBy:function(e,t){return Gt(this,Wt(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return Gt(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return Gt(this,function(e,t,n){var r=Qt(e);return r.__iterateUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterate(r,o);var a=0;return e.__iterate(function(e,o,u){return t.call(n,e,o,u)&&++a&&r(e,o,i)}),a},r.__iteratorUncached=function(r,o){var i=this;if(o)return this.cacheResult().__iterator(r,o);var a=e.__iterator(N,o),u=!0;return new U(function(){if(!u)return{value:void 0,done:!0};var e=a.next();if(e.done)return e;var o=e.value,s=o[0],l=o[1];return t.call(n,l,s,i)?r===N?e:q(r,s,l,e):(u=!1,{value:void 0,done:!0})})},r}(this,e,t))},takeUntil:function(e,t){return this.takeWhile(Nn(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=function(e){if(e.size===1/0)return 0;var t=c(e),n=u(e),r=t?1:0;return function(e,t){return t=xe(t,3432918353),t=xe(t<<15|t>>>-15,461845907),t=xe(t<<13|t>>>-13,5),t=xe((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=Se((t=xe(t^t>>>13,3266489909))^t>>>16)}(e.__iterate(n?t?function(e,t){r=31*r+qn(Ce(e),Ce(t))|0}:function(e,t){r=r+qn(Ce(e),Ce(t))|0}:t?function(e){r=31*r+Ce(e)|0}:function(e){r=r+Ce(e)|0}),r)}(this))}});var Tn=n.prototype;Tn[f]=!0,Tn[L]=Tn.values,Tn.__toJS=Tn.toArray,Tn.__toStringMapper=Dn,Tn.inspect=Tn.toSource=function(){return this.toString()},Tn.chain=Tn.flatMap,Tn.contains=Tn.includes,Pn(r,{flip:function(){return Gt(this,Ut(this))},mapEntries:function(e,t){var n=this,r=0;return Gt(this,this.toSeq().map(function(o,i){return e.call(t,[i,o],r++,n)}).fromEntrySeq())},mapKeys:function(e,t){var n=this;return Gt(this,this.toSeq().flip().map(function(r,o){return e.call(t,r,o,n)}).flip())}});var Mn=r.prototype;function In(e,t){return t}function jn(e,t){return[t,e]}function Nn(e){return function(){return!e.apply(this,arguments)}}function Rn(e){return function(){return-e.apply(this,arguments)}}function Dn(e){return"string"==typeof e?JSON.stringify(e):String(e)}function Ln(){return S(arguments)}function Un(e,t){return et?-1:0}function qn(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return Mn[p]=!0,Mn[L]=Tn.entries,Mn.__toJS=Tn.toObject,Mn.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+Dn(e)},Pn(o,{toKeyedSeq:function(){return new Nt(this,!1)},filter:function(e,t){return Gt(this,zt(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return Gt(this,Ft(this,!1))},slice:function(e,t){return Gt(this,Bt(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=P(e,e<0?this.count():this.size);var r=this.slice(0,e);return Gt(this,1===n?r:r.concat(S(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return Gt(this,Ht(this,e,!1))},get:function(e,t){return(e=k(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find(function(t,n){return n===e},void 0,t)},has:function(e){return(e=k(this,e))>=0&&(void 0!==this.size?this.size===1/0||e5e3)return e.textContent;return function(e){for(var n,r,o,i,a,u=e.textContent,s=0,l=u[0],c=1,f=e.innerHTML="",p=0;r=n,n=p<7&&"\\"==n?1:c;){if(c=l,l=u[++s],i=f.length>1,!c||p>8&&"\n"==c||[/\S/.test(c),1,1,!/[$\w]/.test(c),("/"==n||"\n"==n)&&i,'"'==n&&i,"'"==n&&i,u[s-4]+r+n=="--\x3e",r+n=="*/"][p])for(f&&(e.appendChild(a=t.createElement("span")).setAttribute("style",["color: #555; font-weight: bold;","","","color: #555;",""][p?p<3?2:p>6?4:p>3?3:+/^(a(bstract|lias|nd|rguments|rray|s(m|sert)?|uto)|b(ase|egin|ool(ean)?|reak|yte)|c(ase|atch|har|hecked|lass|lone|ompl|onst|ontinue)|de(bugger|cimal|clare|f(ault|er)?|init|l(egate|ete)?)|do|double|e(cho|ls?if|lse(if)?|nd|nsure|num|vent|x(cept|ec|p(licit|ort)|te(nds|nsion|rn)))|f(allthrough|alse|inal(ly)?|ixed|loat|or(each)?|riend|rom|unc(tion)?)|global|goto|guard|i(f|mp(lements|licit|ort)|n(it|clude(_once)?|line|out|stanceof|t(erface|ernal)?)?|s)|l(ambda|et|ock|ong)|m(icrolight|odule|utable)|NaN|n(amespace|ative|ext|ew|il|ot|ull)|o(bject|perator|r|ut|verride)|p(ackage|arams|rivate|rotected|rotocol|ublic)|r(aise|e(adonly|do|f|gister|peat|quire(_once)?|scue|strict|try|turn))|s(byte|ealed|elf|hort|igned|izeof|tatic|tring|truct|ubscript|uper|ynchronized|witch)|t(emplate|hen|his|hrows?|ransient|rue|ry|ype(alias|def|id|name|of))|u(n(checked|def(ined)?|ion|less|signed|til)|se|sing)|v(ar|irtual|oid|olatile)|w(char_t|hen|here|hile|ith)|xor|yield)$/.test(f):0]),a.appendChild(t.createTextNode(f))),o=p&&p<7?p:o,f="",p=11;![1,/[\/{}[(\-+*=<>:;|\\.,?!&@~]/.test(c),/[\])]/.test(c),/[$\w]/.test(c),"/"==c&&o<2&&"<"!=n,'"'==c,"'"==c,c+l+u[s+1]+u[s+2]=="\x3c!--",c+l=="/*",c+l=="//","#"==c][--p];);f+=c}}(e)},t.mapToList=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"key";var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:l.default.Map();if(!l.default.Map.isMap(t)||!t.size)return l.default.List();Array.isArray(n)||(n=[n]);if(n.length<1)return t.merge(r);var a=l.default.List();var u=n[0];var s=!0;var c=!1;var f=void 0;try{for(var p,d=(0,i.default)(t.entries());!(s=(p=d.next()).done);s=!0){var h=p.value,v=(0,o.default)(h,2),m=v[0],g=v[1],y=e(g,n.slice(1),r.set(u,m));a=l.default.List.isList(y)?a.concat(y):a.push(y)}}catch(e){c=!0,f=e}finally{try{!s&&d.return&&d.return()}finally{if(c)throw f}}return a},t.extractFileNameFromContentDispositionHeader=function(e){var t=void 0;if([/filename\*=[^']+'\w*'"([^"]+)";?/i,/filename\*=[^']+'\w*'([^;]+);?/i,/filename="([^;]*);?"/i,/filename=([^;]*);?/i].some(function(n){return null!==(t=n.exec(e))}),null!==t&&t.length>1)try{return decodeURIComponent(t[1])}catch(e){console.error(e)}return null},t.pascalCase=C,t.pascalCaseFilename=function(e){return C(e.replace(/\.[^./]*$/,""))},t.sanitizeUrl=function(e){if("string"!=typeof e||""===e)return"";return(0,c.sanitizeUrl)(e)},t.getAcceptControllingResponse=function(e){if(!l.default.OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;var t=e.find(function(e,t){return t.startsWith("2")&&(0,u.default)(e.get("content")||{}).length>0}),n=e.get("default")||l.default.OrderedMap(),r=(n.get("content")||l.default.OrderedMap()).keySeq().toJS().length?n:null;return t||r},t.deeplyStripKey=function e(t,n){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:function(){return!0};if("object"!==(void 0===t?"undefined":(0,s.default)(t))||Array.isArray(t)||null===t||!n)return t;var o=(0,a.default)({},t);(0,u.default)(o).forEach(function(t){t===n&&r(o[t],t)?delete o[t]:o[t]=e(o[t],n,r)});return o},t.stringify=function(e){if("string"==typeof e)return e;e.toJS&&(e=e.toJS());if("object"===(void 0===e?"undefined":(0,s.default)(e))&&null!==e)try{return(0,r.default)(e,null,2)}catch(t){return String(e)}return e.toString()},t.numberToString=function(e){if("number"==typeof e)return e.toString();return e},t.paramToIdentifier=q,t.paramToValue=function(e,t){return q(e,{returnAll:!0}).map(function(e){return t[e]}).filter(function(e){return void 0!==e})[0]};var l=_(n(7)),c=n(572),f=_(n(573)),p=_(n(281)),d=_(n(285)),h=_(n(288)),v=_(n(651)),m=_(n(105)),g=n(194),y=_(n(32)),b=_(n(724));function _(e){return e&&e.__esModule?e:{default:e}}var w="default",E=t.isImmutable=function(e){return l.default.Iterable.isIterable(e)};function x(e){return Array.isArray(e)?e:[e]}function S(e){return!!e&&"object"===(void 0===e?"undefined":(0,s.default)(e))}t.memoize=d.default;function C(e){return(0,p.default)((0,f.default)(e))}t.propChecker=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:[];return(0,u.default)(e).length!==(0,u.default)(t).length||((0,v.default)(e,function(e,n){if(r.includes(n))return!1;var o=t[n];return l.default.Iterable.isIterable(e)?!l.default.is(e,o):("object"!==(void 0===e?"undefined":(0,s.default)(e))||"object"!==(void 0===o?"undefined":(0,s.default)(o)))&&e!==o})||n.some(function(n){return!(0,m.default)(e[n],t[n])}))};var k=t.validateMaximum=function(e,t){if(e>t)return"Value must be less than Maximum"},A=t.validateMinimum=function(e,t){if(et)return"Value must be less than MaxLength"},D=t.validateMinLength=function(e,t){if(e.length2&&void 0!==arguments[2]?arguments[2]:{},r=n.isOAS3,o=void 0!==r&&r,i=n.bypassRequiredCheck,a=void 0!==i&&i,u=[],c=e.get("required"),f=o?e.get("schema"):e;if(!f)return u;var p=f.get("maximum"),d=f.get("minimum"),h=f.get("type"),v=f.get("format"),m=f.get("maxLength"),g=f.get("minLength"),b=f.get("pattern");if(h&&(c||t)){var _="string"===h&&t,w="array"===h&&Array.isArray(t)&&t.length,E="array"===h&&l.default.List.isList(t)&&t.count(),x="file"===h&&t instanceof y.default.File,S="boolean"===h&&(t||!1===t),C="number"===h&&(t||0===t),U="integer"===h&&(t||0===t),q=!1;if(o&&"object"===h)if("object"===(void 0===t?"undefined":(0,s.default)(t)))q=!0;else if("string"==typeof t)try{JSON.parse(t),q=!0}catch(e){return u.push("Parameter string value must be valid JSON"),u}var F=[_,w,E,x,S,C,U,q].some(function(e){return!!e});if(c&&!F&&!a)return u.push("Required field is not provided"),u;if(b){var z=L(t,b);z&&u.push(z)}if(m||0===m){var B=R(t,m);B&&u.push(B)}if(g){var V=D(t,g);V&&u.push(V)}if(p||0===p){var H=k(t,p);H&&u.push(H)}if(d||0===d){var W=A(t,d);W&&u.push(W)}if("string"===h){var J=void 0;if(!(J="date-time"===v?j(t):"uuid"===v?N(t):I(t)))return u;u.push(J)}else if("boolean"===h){var Y=M(t);if(!Y)return u;u.push(Y)}else if("number"===h){var K=O(t);if(!K)return u;u.push(K)}else if("integer"===h){var G=P(t);if(!G)return u;u.push(G)}else if("array"===h){var $;if(!E||!t.count())return u;$=f.getIn(["items","type"]),t.forEach(function(e,t){var n=void 0;"number"===$?n=O(e):"integer"===$?n=P(e):"string"===$&&(n=I(e)),n&&u.push({index:t,error:n})})}else if("file"===h){var Z=T(t);if(!Z)return u;u.push(Z)}}return u},t.getSampleSchema=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(/xml/.test(t)){if(!e.xml||!e.xml.name){if(e.xml=e.xml||{},!e.$$ref)return e.type||e.items||e.properties||e.additionalProperties?'\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e':null;var o=e.$$ref.match(/\S*\/(\S+)$/);e.xml.name=o[1]}return(0,g.memoizedCreateXMLExample)(e,n)}var i=(0,g.memoizedSampleFromSchema)(e,n);return"object"===(void 0===i?"undefined":(0,s.default)(i))?(0,r.default)(i,null,2):i},t.parseSearch=function(){var e={},t=y.default.location.search;if(!t)return{};if(""!=t){var n=t.substr(1).split("&");for(var r in n)n.hasOwnProperty(r)&&(r=n[r].split("="),e[decodeURIComponent(r[0])]=r[1]&&decodeURIComponent(r[1])||"")}return e},t.serializeSearch=function(e){return(0,u.default)(e).map(function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])}).join("&")},t.btoa=function(t){return(t instanceof e?t:new e(t.toString(),"utf-8")).toString("base64")},t.sorters={operationsSorter:{alpha:function(e,t){return e.get("path").localeCompare(t.get("path"))},method:function(e,t){return e.get("method").localeCompare(t.get("method"))}},tagsSorter:{alpha:function(e,t){return e.localeCompare(t)}}},t.buildFormData=function(e){var t=[];for(var n in e){var r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},t.shallowEqualKeys=function(e,t,n){return!!(0,h.default)(n,function(n){return(0,m.default)(e[n],t[n])})};var U=t.createDeepLinkPath=function(e){return"string"==typeof e||e instanceof String?e.trim().replace(/\s/g,"%20"):""};t.escapeDeepLinkPath=function(e){return(0,b.default)(U(e).replace(/%20/g,"_"))},t.getExtensions=function(e){return e.filter(function(e,t){return/^x-/.test(t)})},t.getCommonExtensions=function(e){return e.filter(function(e,t){return/^pattern|maxLength|minLength|maximum|minimum/.test(t)})};function q(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.returnAll,r=void 0!==n&&n,o=t.allowHashes,i=void 0===o||o;if(!l.default.Map.isMap(e))throw new Error("paramToIdentifier: received a non-Im.Map parameter as input");var a=e.get("name"),u=e.get("in"),s=[];return e&&e.hashCode&&u&&a&&i&&s.push(u+"."+a+".hash-"+e.hashCode()),u&&a&&s.push(u+"."+a),s.push(a),r?s:s[0]||""}}).call(t,n(54).Buffer)},function(e,t,n){"use strict";var r=n(34);e.exports=r},function(e,t,n){"use strict";e.exports=function(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r>",i={listOf:function(e){return l(e,"List",r.List.isList)},mapOf:function(e,t){return c(e,t,"Map",r.Map.isMap)},orderedMapOf:function(e,t){return c(e,t,"OrderedMap",r.OrderedMap.isOrderedMap)},setOf:function(e){return l(e,"Set",r.Set.isSet)},orderedSetOf:function(e){return l(e,"OrderedSet",r.OrderedSet.isOrderedSet)},stackOf:function(e){return l(e,"Stack",r.Stack.isStack)},iterableOf:function(e){return l(e,"Iterable",r.Iterable.isIterable)},recordOf:function(e){return u(function(t,n,o,i,u){for(var s=arguments.length,l=Array(s>5?s-5:0),c=5;c6?s-6:0),c=6;c5?l-5:0),f=5;f5?i-5:0),u=5;u key("+c[f]+")"].concat(a));if(d instanceof Error)return d}})).apply(void 0,i);var s})}function f(e){var t=void 0===arguments[1]?"Iterable":arguments[1],n=void 0===arguments[2]?r.Iterable.isIterable:arguments[2];return u(function(r,o,i,u,s){for(var l=arguments.length,c=Array(l>5?l-5:0),f=5;f?@[\]^_`{|}~-])/g;function a(e){return!(e>=55296&&e<=57343)&&(!(e>=64976&&e<=65007)&&(65535!=(65535&e)&&65534!=(65535&e)&&(!(e>=0&&e<=8)&&(11!==e&&(!(e>=14&&e<=31)&&(!(e>=127&&e<=159)&&!(e>1114111)))))))}function u(e){if(e>65535){var t=55296+((e-=65536)>>10),n=56320+(1023&e);return String.fromCharCode(t,n)}return String.fromCharCode(e)}var s=/&([a-z#][a-z0-9]{1,31});/gi,l=/^#((?:x[a-f0-9]{1,8}|[0-9]{1,8}))/i,c=n(417);function f(e,t){var n=0;return o(c,t)?c[t]:35===t.charCodeAt(0)&&l.test(t)&&a(n="x"===t[1].toLowerCase()?parseInt(t.slice(2),16):parseInt(t.slice(1),10))?u(n):e}var p=/[&<>"]/,d=/[&<>"]/g,h={"&":"&","<":"<",">":">",'"':"""};function v(e){return h[e]}t.assign=function(e){return[].slice.call(arguments,1).forEach(function(t){if(t){if("object"!=typeof t)throw new TypeError(t+"must be object");Object.keys(t).forEach(function(n){e[n]=t[n]})}}),e},t.isString=function(e){return"[object String]"===function(e){return Object.prototype.toString.call(e)}(e)},t.has=o,t.unescapeMd=function(e){return e.indexOf("\\")<0?e:e.replace(i,"$1")},t.isValidEntityCode=a,t.fromCodePoint=u,t.replaceEntities=function(e){return e.indexOf("&")<0?e:e.replace(s,f)},t.escapeHtml=function(e){return p.test(e)?e.replace(d,v):e}},function(e,t){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},function(e,t,n){var r=n(33),o=n(61),i=n(59),a=n(73),u=n(120),s=function(e,t,n){var l,c,f,p,d=e&s.F,h=e&s.G,v=e&s.S,m=e&s.P,g=e&s.B,y=h?r:v?r[t]||(r[t]={}):(r[t]||{}).prototype,b=h?o:o[t]||(o[t]={}),_=b.prototype||(b.prototype={});for(l in h&&(n=t),n)f=((c=!d&&y&&void 0!==y[l])?y:n)[l],p=g&&c?u(f,r):m&&"function"==typeof f?u(Function.call,f):f,y&&a(y,l,f,e&s.U),b[l]!=f&&i(b,l,p),m&&_[l]!=f&&(_[l]=f)};r.core=o,s.F=1,s.G=2,s.S=4,s.P=8,s.B=16,s.W=32,s.U=64,s.R=128,e.exports=s},function(e,t,n){var r=n(29),o=n(101),i=n(53),a=/"/g,u=function(e,t,n,r){var o=String(i(e)),u="<"+t;return""!==n&&(u+=" "+n+'="'+String(r).replace(a,""")+'"'),u+">"+o+""};e.exports=function(e,t){var n={};n[e]=t(u),r(r.P+r.F*o(function(){var t=""[e]('"');return t!==t.toLowerCase()||t.split('"').length>3}),"String",n)}},function(e,t){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";var r,o=n(91),i=(r=o)&&r.__esModule?r:{default:r};e.exports=function(){var e={location:{},history:{},open:function(){},close:function(){},File:function(){}};if("undefined"==typeof window)return e;try{e=window;var t=!0,n=!1,r=void 0;try{for(var o,a=(0,i.default)(["File","Blob","FormData"]);!(t=(o=a.next()).done);t=!0){var u=o.value;u in window&&(e[u]=window[u])}}catch(e){n=!0,r=e}finally{try{!t&&a.return&&a.return()}finally{if(n)throw r}}}catch(e){console.error(e)}return e}()},function(e,t){var n=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=n)},function(e,t,n){"use strict";function r(e){return function(){return e}}var o=function(){};o.thatReturns=r,o.thatReturnsFalse=r(!1),o.thatReturnsTrue=r(!0),o.thatReturnsNull=r(null),o.thatReturnsThis=function(){return this},o.thatReturnsArgument=function(e){return e},e.exports=o},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=i(n(25));t.isOAS3=a,t.isSwagger2=function(e){var t=e.get("swagger");if("string"!=typeof t)return!1;return t.startsWith("2.0")},t.OAS3ComponentWrapFactory=function(e){return function(t,n){return function(i){if(n&&n.specSelectors&&n.specSelectors.specJson){var u=n.specSelectors.specJson();return a(u)?o.default.createElement(e,(0,r.default)({},i,n,{Ori:t})):o.default.createElement(t,i)}return console.warn("OAS3 wrapper: couldn't get spec"),null}}};var o=i(n(0));function i(e){return e&&e.__esModule?e:{default:e}}function a(e){var t=e.get("openapi");return"string"==typeof t&&(t.startsWith("3.0.")&&t.length>4)}},function(e,t,n){var r=n(28);e.exports=function(e){if(!r(e))throw TypeError(e+" is not an object!");return e}},function(e,t,n){var r=n(279),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();e.exports=i},function(e,t){e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},function(e,t,n){"use strict";var r=null;e.exports={debugTool:r}},function(e,t,n){var r=n(36),o=n(239),i=n(158),a=Object.defineProperty;t.f=n(44)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t,n){e.exports={default:n(517),__esModule:!0}},function(e,t,n){e.exports={default:n(518),__esModule:!0}},function(e,t,n){"use strict";var r=n(11),o=n(13),i=n(354),a=n(69),u=n(355),s=n(88),l=n(148),c=n(8),f=[],p=0,d=i.getPooled(),h=!1,v=null;function m(){E.ReactReconcileTransaction&&v||r("123")}var g=[{initialize:function(){this.dirtyComponentsLength=f.length},close:function(){this.dirtyComponentsLength!==f.length?(f.splice(0,this.dirtyComponentsLength),w()):f.length=0}},{initialize:function(){this.callbackQueue.reset()},close:function(){this.callbackQueue.notifyAll()}}];function y(){this.reinitializeTransaction(),this.dirtyComponentsLength=null,this.callbackQueue=i.getPooled(),this.reconcileTransaction=E.ReactReconcileTransaction.getPooled(!0)}function b(e,t){return e._mountOrder-t._mountOrder}function _(e){var t=e.dirtyComponentsLength;t!==f.length&&r("124",t,f.length),f.sort(b),p++;for(var n=0;n + * @license MIT + */ +var r=n(529),o=n(530),i=n(262);function a(){return s.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function u(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function h(e,t){if(s.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return z(e).length;default:if(r)return F(e).length;t=(""+t).toLowerCase(),r=!0}}function v(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function m(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=s.from(t,r)),s.isBuffer(t))return 0===t.length?-1:g(e,t,n,r,o);if("number"==typeof t)return t&=255,s.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):g(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function g(e,t,n,r,o){var i,a=1,u=e.length,s=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,u/=2,s/=2,n/=2}function l(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var c=-1;for(i=n;iu&&(n=u-s),i=n;i>=0;i--){for(var f=!0,p=0;po&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function S(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function C(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:l>223?3:l>191?2:1;if(o+f<=n)switch(f){case 1:l<128&&(c=l);break;case 2:128==(192&(i=e[o+1]))&&(s=(31&l)<<6|63&i)>127&&(c=s);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(s=(15&l)<<12|(63&i)<<6|63&a)>2047&&(s<55296||s>57343)&&(c=s);break;case 4:i=e[o+1],a=e[o+2],u=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&u)&&(s=(15&l)<<18|(63&i)<<12|(63&a)<<6|63&u)>65535&&s<1114112&&(c=s)}null===c?(c=65533,f=1):c>65535&&(c-=65536,r.push(c>>>10&1023|55296),c=56320|1023&c),r.push(c),o+=f}return function(e){var t=e.length;if(t<=k)return String.fromCharCode.apply(String,e);var n="",r=0;for(;rthis.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return P(this,t,n);case"utf8":case"utf-8":return C(this,t,n);case"ascii":return A(this,t,n);case"latin1":case"binary":return O(this,t,n);case"base64":return S(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return T(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}.apply(this,arguments)},s.prototype.equals=function(e){if(!s.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===s.compare(this,e)},s.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},s.prototype.compare=function(e,t,n,r,o){if(!s.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(t>>>=0,n>>>=0,r>>>=0,o>>>=0,this===e)return 0;for(var i=o-r,a=n-t,u=Math.min(i,a),l=this.slice(r,o),c=e.slice(t,n),f=0;fo)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return y(this,e,t,n);case"utf8":case"utf-8":return b(this,e,t,n);case"ascii":return _(this,e,t,n);case"latin1":case"binary":return w(this,e,t,n);case"base64":return E(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return x(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},s.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var k=4096;function A(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function I(e,t,n,r,o,i){if(!s.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function j(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function N(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function R(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function D(e,t,n,r,i){return i||R(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function L(e,t,n,r,i){return i||R(e,0,n,8),o.write(e,t,n,r,52,8),n+8}s.prototype.slice=function(e,t){var n,r=this.length;if(e=~~e,t=void 0===t?r:~~t,e<0?(e+=r)<0&&(e=0):e>r&&(e=r),t<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},s.prototype.readUInt8=function(e,t){return t||M(e,1,this.length),this[e]},s.prototype.readUInt16LE=function(e,t){return t||M(e,2,this.length),this[e]|this[e+1]<<8},s.prototype.readUInt16BE=function(e,t){return t||M(e,2,this.length),this[e]<<8|this[e+1]},s.prototype.readUInt32LE=function(e,t){return t||M(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},s.prototype.readUInt32BE=function(e,t){return t||M(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},s.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||M(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},s.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||M(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},s.prototype.readInt8=function(e,t){return t||M(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},s.prototype.readInt16LE=function(e,t){t||M(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},s.prototype.readInt16BE=function(e,t){t||M(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},s.prototype.readInt32LE=function(e,t){return t||M(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},s.prototype.readInt32BE=function(e,t){return t||M(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},s.prototype.readFloatLE=function(e,t){return t||M(e,4,this.length),o.read(this,e,!0,23,4)},s.prototype.readFloatBE=function(e,t){return t||M(e,4,this.length),o.read(this,e,!1,23,4)},s.prototype.readDoubleLE=function(e,t){return t||M(e,8,this.length),o.read(this,e,!0,52,8)},s.prototype.readDoubleBE=function(e,t){return t||M(e,8,this.length),o.read(this,e,!1,52,8)},s.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||I(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},s.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,1,255,0),s.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},s.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,65535,0),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},s.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,65535,0),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},s.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,4294967295,0),s.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):N(this,e,t,!0),t+4},s.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,4294967295,0),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},s.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);I(this,e,t,n,o-1,-o)}var i=0,a=1,u=0;for(this[t]=255&e;++i>0)-u&255;return t+n},s.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);I(this,e,t,n,o-1,-o)}var i=n-1,a=1,u=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===u&&0!==this[t+i+1]&&(u=1),this[t+i]=(e/a>>0)-u&255;return t+n},s.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,1,127,-128),s.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},s.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,32767,-32768),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):j(this,e,t,!0),t+2},s.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,2,32767,-32768),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):j(this,e,t,!1),t+2},s.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,2147483647,-2147483648),s.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):N(this,e,t,!0),t+4},s.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||I(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),s.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):N(this,e,t,!1),t+4},s.prototype.writeFloatLE=function(e,t,n){return D(this,e,t,!0,n)},s.prototype.writeFloatBE=function(e,t,n){return D(this,e,t,!1,n)},s.prototype.writeDoubleLE=function(e,t,n){return L(this,e,t,!0,n)},s.prototype.writeDoubleBE=function(e,t,n){return L(this,e,t,!1,n)},s.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!s.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function z(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(U,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function B(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(t,n(31))},function(e,t,n){var r=n(278);e.exports=function(e){return null==e?"":r(e)}},function(e,t){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function u(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var s,l=[],c=!1,f=-1;function p(){c&&s&&(c=!1,s.length?l=s.concat(l):f=-1,l.length&&d())}function d(){if(!c){var e=u(p);c=!0;for(var t=l.length;t;){for(s=l,l=[];++f1)for(var n=1;n1?t-1:0),r=1;r2?n-2:0),o=2;o1){for(var h=Array(d),v=0;v1){for(var g=Array(m),y=0;y=0||Object.prototype.hasOwnProperty.call(e,r)&&(n[r]=e[r]);return n}},function(e,t,n){"use strict";function r(e){return void 0===e||null===e}e.exports.isNothing=r,e.exports.isObject=function(e){return"object"==typeof e&&null!==e},e.exports.toArray=function(e){return Array.isArray(e)?e:r(e)?[]:[e]},e.exports.repeat=function(e,t){var n,r="";for(n=0;n=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t){var n={}.toString;e.exports=function(e){return n.call(e).slice(8,-1)}},function(e,t,n){e.exports=!n(101)(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},function(e,t){e.exports=function(e){try{return!!e()}catch(e){return!0}}},function(e,t){e.exports={}},function(e,t,n){var r=n(119),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},function(e,t,n){"use strict";e.exports=function(e){for(var t=arguments.length-1,n="Minified React error #"+e+"; visit http://facebook.github.io/react/docs/error-decoder.html?invariant="+e,r=0;r0?o(r(e),9007199254740991):0}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t,n){var r=n(60),o=n(460),i=n(461),a=Object.defineProperty;t.f=n(100)?Object.defineProperty:function(e,t,n){if(r(e),t=i(t,!0),r(n),o)try{return a(e,t,n)}catch(e){}if("get"in n||"set"in n)throw TypeError("Accessors not supported!");return"value"in n&&(e[t]=n.value),e}},function(e,t){var n={}.hasOwnProperty;e.exports=function(e,t){return n.call(e,t)}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(121);e.exports=function(e,t,n){if(r(e),void 0===t)return e;switch(n){case 1:return function(n){return e.call(t,n)};case 2:return function(n,r){return e.call(t,n,r)};case 3:return function(n,r,o){return e.call(t,n,r,o)}}return function(){return e.apply(t,arguments)}}},function(e,t){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},function(e,t,n){var r=n(466),o=n(53);e.exports=function(e){return r(o(e))}},function(e,t,n){"use strict";var r=n(59),o=n(73),i=n(101),a=n(53),u=n(18);e.exports=function(e,t,n){var s=u(e),l=n(a,s,""[e]),c=l[0],f=l[1];i(function(){var t={};return t[s]=function(){return 7},7!=""[e](t)})&&(o(String.prototype,e,c),r(RegExp.prototype,s,2==t?function(e,t){return f.call(e,this,t)}:function(e){return f.call(e,this)}))}},function(e,t,n){var r=n(116)("meta"),o=n(28),i=n(52),a=n(40).f,u=0,s=Object.isExtensible||function(){return!0},l=!n(51)(function(){return s(Object.preventExtensions({}))}),c=function(e){a(e,r,{value:{i:"O"+ ++u,w:{}}})},f=e.exports={KEY:r,NEED:!1,fastKey:function(e,t){if(!o(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!i(e,r)){if(!s(e))return"F";if(!t)return"E";c(e)}return e[r].i},getWeak:function(e,t){if(!i(e,r)){if(!s(e))return!0;if(!t)return!1;c(e)}return e[r].w},onFreeze:function(e){return l&&f.NEED&&s(e)&&!i(e,r)&&c(e),e}}},function(e,t){t.f={}.propertyIsEnumerable},function(e,t,n){"use strict";var r={};e.exports=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CLEAR_BY=t.CLEAR=t.NEW_AUTH_ERR=t.NEW_SPEC_ERR_BATCH=t.NEW_SPEC_ERR=t.NEW_THROWN_ERR_BATCH=t.NEW_THROWN_ERR=void 0,t.newThrownErr=function(e){return{type:a,payload:(0,i.default)(e)}},t.newThrownErrBatch=function(e){return{type:u,payload:e}},t.newSpecErr=function(e){return{type:s,payload:e}},t.newSpecErrBatch=function(e){return{type:l,payload:e}},t.newAuthErr=function(e){return{type:c,payload:e}},t.clear=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{type:f,payload:e}},t.clearBy=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!0};return{type:p,payload:e}};var r,o=n(180),i=(r=o)&&r.__esModule?r:{default:r};var a=t.NEW_THROWN_ERR="err_new_thrown_err",u=t.NEW_THROWN_ERR_BATCH="err_new_thrown_err_batch",s=t.NEW_SPEC_ERR="err_new_spec_err",l=t.NEW_SPEC_ERR_BATCH="err_new_spec_err_batch",c=t.NEW_AUTH_ERR="err_new_auth_err",f=t.CLEAR="err_clear",p=t.CLEAR_BY="err_clear_by"},function(e,t,n){var r=n(62),o=n(47),i="[object Symbol]";e.exports=function(e){return"symbol"==typeof e||o(e)&&r(e)==i}},function(e,t,n){var r=n(63)(Object,"create");e.exports=r},function(e,t,n){var r=n(601),o=n(602),i=n(603),a=n(604),u=n(605);function s(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1&&e%1==0&&e1&&void 0!==arguments[1]?arguments[1]:"";if(u.List.isList(e))return e.some(function(e){return u.Map.isMap(e)&&e.get("in")===t})},t.parametersIncludeType=T,t.contentTypeValues=function(e,t){t=t||[];var n=d(e).getIn(["paths"].concat((0,o.default)(t)),(0,u.fromJS)({})),r=e.getIn(["meta","paths"].concat((0,o.default)(t)),(0,u.fromJS)({})),i=M(e,t),a=n.get("parameters")||new u.List,s=r.get("consumes_value")?r.get("consumes_value"):T(a,"file")?"multipart/form-data":T(a,"formData")?"application/x-www-form-urlencoded":void 0;return(0,u.fromJS)({requestContentType:s,responseContentType:i})},t.currentProducesFor=M,t.producesOptionsFor=function(e,t){t=t||[];var n=d(e),i=n.getIn(["paths"].concat((0,o.default)(t)),null);if(null===i)return;var a=t,u=(0,r.default)(a,1)[0],s=i.get("produces",null),l=n.getIn(["paths",u,"produces"],null),c=n.getIn(["produces"],null);return s||l||c},t.consumesOptionsFor=function(e,t){t=t||[];var n=d(e),i=n.getIn(["paths"].concat((0,o.default)(t)),null);if(null===i)return;var a=t,u=(0,r.default)(a,1)[0],s=i.get("consumes",null),l=n.getIn(["paths",u,"consumes"],null),c=n.getIn(["consumes"],null);return s||l||c};var i=n(58),a=n(9),u=n(7);function s(e){return e&&e.__esModule?e:{default:e}}var l=["get","put","post","delete","options","head","patch","trace"],c=function(e){return e||(0,u.Map)()},f=(t.lastError=(0,i.createSelector)(c,function(e){return e.get("lastError")}),t.url=(0,i.createSelector)(c,function(e){return e.get("url")}),t.specStr=(0,i.createSelector)(c,function(e){return e.get("spec")||""}),t.specSource=(0,i.createSelector)(c,function(e){return e.get("specSource")||"not-editor"}),t.specJson=(0,i.createSelector)(c,function(e){return e.get("json",(0,u.Map)())})),p=(t.specResolved=(0,i.createSelector)(c,function(e){return e.get("resolved",(0,u.Map)())}),t.specResolvedSubtree=function(e,t){return e.getIn(["resolvedSubtrees"].concat((0,o.default)(t)),void 0)},function e(t,n){return u.Map.isMap(t)&&u.Map.isMap(n)?n.get("$$ref")?n:(0,u.OrderedMap)().mergeWith(e,t,n):n}),d=t.specJsonWithResolvedSubtrees=(0,i.createSelector)(c,function(e){return(0,u.OrderedMap)().mergeWith(p,e.get("json"),e.get("resolvedSubtrees"))}),h=t.spec=function(e){return f(e)},v=(t.isOAS3=(0,i.createSelector)(h,function(){return!1}),t.info=(0,i.createSelector)(h,function(e){return j(e&&e.get("info"))})),m=(t.externalDocs=(0,i.createSelector)(h,function(e){return j(e&&e.get("externalDocs"))}),t.version=(0,i.createSelector)(v,function(e){return e&&e.get("version")})),g=(t.semver=(0,i.createSelector)(m,function(e){return/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(e).slice(1)}),t.paths=(0,i.createSelector)(d,function(e){return e.get("paths")})),y=t.operations=(0,i.createSelector)(g,function(e){if(!e||e.size<1)return(0,u.List)();var t=(0,u.List)();return e&&e.forEach?(e.forEach(function(e,n){if(!e||!e.forEach)return{};e.forEach(function(e,r){l.indexOf(r)<0||(t=t.push((0,u.fromJS)({path:n,method:r,operation:e,id:r+"-"+n})))})}),t):(0,u.List)()}),b=t.consumes=(0,i.createSelector)(h,function(e){return(0,u.Set)(e.get("consumes"))}),_=t.produces=(0,i.createSelector)(h,function(e){return(0,u.Set)(e.get("produces"))}),w=(t.security=(0,i.createSelector)(h,function(e){return e.get("security",(0,u.List)())}),t.securityDefinitions=(0,i.createSelector)(h,function(e){return e.get("securityDefinitions")}),t.findDefinition=function(e,t){var n=e.getIn(["resolvedSubtrees","definitions",t],null),r=e.getIn(["json","definitions",t],null);return n||r||null},t.definitions=(0,i.createSelector)(h,function(e){var t=e.get("definitions");return u.Map.isMap(t)?t:(0,u.Map)()}),t.basePath=(0,i.createSelector)(h,function(e){return e.get("basePath")}),t.host=(0,i.createSelector)(h,function(e){return e.get("host")}),t.schemes=(0,i.createSelector)(h,function(e){return e.get("schemes",(0,u.Map)())}),t.operationsWithRootInherited=(0,i.createSelector)(y,b,_,function(e,t,n){return e.map(function(e){return e.update("operation",function(e){if(e){if(!u.Map.isMap(e))return;return e.withMutations(function(e){return e.get("consumes")||e.update("consumes",function(e){return(0,u.Set)(e).merge(t)}),e.get("produces")||e.update("produces",function(e){return(0,u.Set)(e).merge(n)}),e})}return(0,u.Map)()})})})),E=t.tags=(0,i.createSelector)(h,function(e){var t=e.get("tags",(0,u.List)());return u.List.isList(t)?t.filter(function(e){return u.Map.isMap(e)}):(0,u.List)()}),x=t.tagDetails=function(e,t){return(E(e)||(0,u.List)()).filter(u.Map.isMap).find(function(e){return e.get("name")===t},(0,u.Map)())},S=t.operationsWithTags=(0,i.createSelector)(w,E,function(e,t){return e.reduce(function(e,t){var n=(0,u.Set)(t.getIn(["operation","tags"]));return n.count()<1?e.update("default",(0,u.List)(),function(e){return e.push(t)}):n.reduce(function(e,n){return e.update(n,(0,u.List)(),function(e){return e.push(t)})},e)},t.reduce(function(e,t){return e.set(t.get("name"),(0,u.List)())},(0,u.OrderedMap)()))}),C=(t.taggedOperations=function(e){return function(t){var n=(0,t.getConfigs)(),r=n.tagsSorter,o=n.operationsSorter;return S(e).sortBy(function(e,t){return t},function(e,t){var n="function"==typeof r?r:a.sorters.tagsSorter[r];return n?n(e,t):null}).map(function(t,n){var r="function"==typeof o?o:a.sorters.operationsSorter[o],i=r?t.sort(r):t;return(0,u.Map)({tagDetails:x(e,n),operations:i})})}},t.responses=(0,i.createSelector)(c,function(e){return e.get("responses",(0,u.Map)())})),k=t.requests=(0,i.createSelector)(c,function(e){return e.get("requests",(0,u.Map)())}),A=t.mutatedRequests=(0,i.createSelector)(c,function(e){return e.get("mutatedRequests",(0,u.Map)())}),O=(t.responseFor=function(e,t,n){return C(e).getIn([t,n],null)},t.requestFor=function(e,t,n){return k(e).getIn([t,n],null)},t.mutatedRequestFor=function(e,t,n){return A(e).getIn([t,n],null)},t.allowTryItOutFor=function(){return!0},t.parameterWithMetaByIdentity=function(e,t,n){var r=d(e).getIn(["paths"].concat((0,o.default)(t),["parameters"]),(0,u.OrderedMap)()),i=e.getIn(["meta","paths"].concat((0,o.default)(t),["parameters"]),(0,u.OrderedMap)());return r.map(function(e){var t=i.get(n.get("in")+"."+n.get("name")),r=i.get(n.get("in")+"."+n.get("name")+".hash-"+n.hashCode());return(0,u.OrderedMap)().merge(e,t,r)}).find(function(e){return e.get("in")===n.get("in")&&e.get("name")===n.get("name")},(0,u.OrderedMap)())}),P=(t.parameterInclusionSettingFor=function(e,t,n,r){var i=r+"."+n;return e.getIn(["meta","paths"].concat((0,o.default)(t),["parameter_inclusions",i]),!1)},t.parameterWithMeta=function(e,t,n,r){var i=d(e).getIn(["paths"].concat((0,o.default)(t),["parameters"]),(0,u.OrderedMap)()).find(function(e){return e.get("in")===r&&e.get("name")===n},(0,u.OrderedMap)());return O(e,t,i)},t.operationWithMeta=function(e,t,n){var r=d(e).getIn(["paths",t,n],(0,u.OrderedMap)()),o=e.getIn(["meta","paths",t,n],(0,u.OrderedMap)()),i=r.get("parameters",(0,u.List)()).map(function(r){return O(e,[t,n],r)});return(0,u.OrderedMap)().merge(r,o).set("parameters",i)});t.hasHost=(0,i.createSelector)(h,function(e){var t=e.get("host");return"string"==typeof t&&t.length>0&&"/"!==t[0]});function T(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(u.List.isList(e))return e.some(function(e){return u.Map.isMap(e)&&e.get("type")===t})}function M(e,t){t=t||[];var n=d(e).getIn(["paths"].concat((0,o.default)(t)),null);if(null!==n){var r=e.getIn(["meta","paths"].concat((0,o.default)(t),["produces_value"]),null),i=n.getIn(["produces",0],null);return r||i||"application/json"}}var I=t.operationScheme=function(e,t,n){var r=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),o=Array.isArray(r)?r[1]:null;return e.getIn(["scheme",t,n])||e.getIn(["scheme","_defaultScheme"])||o||""};t.canExecuteScheme=function(e,t,n){return["http","https"].indexOf(I(e,t,n))>-1},t.validateBeforeExecute=function(e,t){t=t||[];var n=!0;return e.getIn(["meta","paths"].concat((0,o.default)(t),["parameters"]),(0,u.fromJS)([])).forEach(function(e){var t=e.get("errors");t&&t.count()&&(n=!1)}),n};function j(e){return u.Map.isMap(e)?e:new u.Map}},function(e,t,n){var r=n(49),o=n(329),i=n(330),a=n(36),u=n(115),s=n(165),l={},c={};(t=e.exports=function(e,t,n,f,p){var d,h,v,m,g=p?function(){return e}:s(e),y=r(n,f,t?2:1),b=0;if("function"!=typeof g)throw TypeError(e+" is not iterable!");if(i(g)){for(d=u(e.length);d>b;b++)if((m=t?y(a(h=e[b])[0],h[1]):y(e[b]))===l||m===c)return m}else for(v=g.call(e);!(h=v.next()).done;)if((m=o(v,y,h.value,t))===l||m===c)return m}).BREAK=l,t.RETURN=c},function(e,t,n){"use strict";var r=n(86);e.exports=r.DEFAULT=new r({include:[n(108)],explicit:[n(758),n(759),n(760)]})},function(e,t,n){var r=n(344),o=n(105),i=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var a=e[t];i.call(e,t)&&o(a,n)&&(void 0!==n||t in e)||r(e,t,n)}},function(e,t,n){"use strict";var r=n(11),o=(n(8),{}),i={reinitializeTransaction:function(){this.transactionWrappers=this.getTransactionWrappers(),this.wrapperInitData?this.wrapperInitData.length=0:this.wrapperInitData=[],this._isInTransaction=!1},_isInTransaction:!1,getTransactionWrappers:null,isInTransaction:function(){return!!this._isInTransaction},perform:function(e,t,n,o,i,a,u,s){var l,c;this.isInTransaction()&&r("27");try{this._isInTransaction=!0,l=!0,this.initializeAll(0),c=e.call(t,n,o,i,a,u,s),l=!1}finally{try{if(l)try{this.closeAll(0)}catch(e){}else this.closeAll(0)}finally{this._isInTransaction=!1}}return c},initializeAll:function(e){for(var t=this.transactionWrappers,n=e;n]/,s=n(219)(function(e,t){if(e.namespaceURI!==i.svg||"innerHTML"in e)e.innerHTML=t;else{(r=r||document.createElement("div")).innerHTML=""+t+"";for(var n=r.firstChild;n.firstChild;)e.appendChild(n.firstChild)}});if(o.canUseDOM){var l=document.createElement("div");l.innerHTML=" ",""===l.innerHTML&&(s=function(e,t){if(e.parentNode&&e.parentNode.replaceChild(e,e),a.test(t)||"<"===t[0]&&u.test(t)){e.innerHTML=String.fromCharCode(65279)+t;var n=e.firstChild;1===n.data.length?e.removeChild(n):n.deleteData(0,1)}else e.innerHTML=t}),l=null}e.exports=s},function(e,t,n){"use strict";var r=/["'&<>]/;e.exports=function(e){return"boolean"==typeof e||"number"==typeof e?""+e:function(e){var t,n=""+e,o=r.exec(n);if(!o)return n;var i="",a=0,u=0;for(a=o.index;adocument.F=Object<\/script>"),e.close(),s=e.F;r--;)delete s.prototype[i[r]];return s()};e.exports=Object.create||function(e,t){var n;return null!==e?(u.prototype=r(e),n=new u,u.prototype=null,n[a]=e):n=s(),void 0===t?n:o(n,t)}},function(e,t){var n=Math.ceil,r=Math.floor;e.exports=function(e){return isNaN(e=+e)?0:(e>0?r:n)(e)}},function(e,t,n){var r=n(163)("keys"),o=n(116);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(21),o=r["__core-js_shared__"]||(r["__core-js_shared__"]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){var r=n(166),o=n(19)("iterator"),i=n(70);e.exports=n(15).getIteratorMethod=function(e){if(void 0!=e)return e[o]||e["@@iterator"]||i[r(e)]}},function(e,t,n){var r=n(93),o=n(19)("toStringTag"),i="Arguments"==r(function(){return arguments}());e.exports=function(e){var t,n,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?n:i?r(t):"Object"==(a=r(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t,n){var r=n(99),o=n(18)("toStringTag"),i="Arguments"==r(function(){return arguments}());e.exports=function(e){var t,n,a;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=Object(e),o))?n:i?r(t):"Object"==(a=r(t))&&"function"==typeof t.callee?"Arguments":a}},function(e,t){var n=0,r=Math.random();e.exports=function(e){return"Symbol(".concat(void 0===e?"":e,")_",(++n+r).toString(36))}},function(e,t,n){var r=n(74),o=n(33).document,i=r(o)&&r(o.createElement);e.exports=function(e){return i?o.createElement(e):{}}},function(e,t,n){var r=n(243)("keys"),o=n(168);e.exports=function(e){return r[e]||(r[e]=o(e))}},function(e,t,n){var r=n(117).f,o=n(118),i=n(18)("toStringTag");e.exports=function(e,t,n){e&&!o(e=n?e:e.prototype,i)&&r(e,i,{configurable:!0,value:t})}},function(e,t,n){"use strict";var r=n(121);e.exports.f=function(e){return new function(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=r}),this.resolve=r(t),this.reject=r(n)}(e)}},function(e,t,n){var r=n(257),o=n(53);e.exports=function(e,t,n){if(r(t))throw TypeError("String#"+n+" doesn't accept regex!");return String(o(e))}},function(e,t,n){var r=n(18)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(n){try{return t[r]=!1,!"/./"[e](t)}catch(e){}}return!0}},function(e,t,n){t.f=n(19)},function(e,t,n){var r=n(21),o=n(15),i=n(114),a=n(175),u=n(40).f;e.exports=function(e){var t=o.Symbol||(o.Symbol=i?{}:r.Symbol||{});"_"==e.charAt(0)||e in t||u(t,e,{value:a.f(e)})}},function(e,t){t.f=Object.getOwnPropertySymbols},function(e,t){},function(e,t,n){"use strict";(function(t){ +/*! + * @description Recursive object extending + * @author Viacheslav Lotsmanov + * @license MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2018 Viacheslav Lotsmanov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +function n(e){return e instanceof t||e instanceof Date||e instanceof RegExp}function r(e){if(e instanceof t){var n=t.alloc?t.alloc(e.length):new t(e.length);return e.copy(n),n}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function o(e,t){return"__proto__"===t?void 0:e[t]}var i=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,a=arguments[0];return Array.prototype.slice.call(arguments,1).forEach(function(u){"object"!=typeof u||null===u||Array.isArray(u)||Object.keys(u).forEach(function(s){return t=o(a,s),(e=o(u,s))===a?void 0:"object"!=typeof e||null===e?void(a[s]=e):Array.isArray(e)?void(a[s]=function e(t){var o=[];return t.forEach(function(t,a){"object"==typeof t&&null!==t?Array.isArray(t)?o[a]=e(t):n(t)?o[a]=r(t):o[a]=i({},t):o[a]=t}),o}(e)):n(e)?void(a[s]=r(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(a[s]=i({},e)):void(a[s]=i(t,e))})}),a}}).call(t,n(54).Buffer)},function(e,t,n){"use strict";e.exports=function(e){return"object"==typeof e?function e(t,n){var r;r=Array.isArray(t)?[]:{};n.push(t);Object.keys(t).forEach(function(o){var i=t[o];"function"!=typeof i&&(i&&"object"==typeof i?-1!==n.indexOf(t[o])?r[o]="[Circular]":r[o]=e(t[o],n.slice(0)):r[o]=i)});"string"==typeof t.name&&(r.name=t.name);"string"==typeof t.message&&(r.message=t.message);"string"==typeof t.stack&&(r.stack=t.stack);return r}(e,[]):"function"==typeof e?"[Function: "+(e.name||"anonymous")+"]":e}},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};function o(e){return null===e?"null":void 0===e?"undefined":"object"===(void 0===e?"undefined":r(e))?Array.isArray(e)?"array":"object":void 0===e?"undefined":r(e)}function i(e){return"object"===o(e)?u(e):"array"===o(e)?a(e):e}function a(e){return e.map(i)}function u(e){var t={};for(var n in e)e.hasOwnProperty(n)&&(t[n]=i(e[n]));return t}function s(e){for(var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[],n={arrayBehaviour:(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).arrayBehaviour||"replace"},r=t.map(function(e){return e||{}}),i=e||{},l=0;l1?t-1:0),r=1;r-1&&e%1==0&&e<=n}},function(e,t){e.exports=function(e){return function(t){return e(t)}}},function(e,t,n){(function(e){var r=n(279),o="object"==typeof t&&t&&!t.nodeType&&t,i=o&&"object"==typeof e&&e&&!e.nodeType&&e,a=i&&i.exports===o&&r.process,u=function(){try{var e=i&&i.require&&i.require("util").types;return e||a&&a.binding&&a.binding("util")}catch(e){}}();e.exports=u}).call(t,n(134)(e))},function(e,t,n){var r=n(24),o=n(128),i=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,a=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||a.test(e)||!i.test(e)||null!=t&&e in Object(t)}},function(e,t){e.exports=function(e){return e}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.memoizedSampleFromSchema=t.memoizedCreateXMLExample=t.sampleXmlFromSchema=t.inferSchema=t.sampleFromSchema=void 0,t.createXMLExample=p;var r=n(9),o=u(n(657)),i=u(n(670)),a=u(n(181));function u(e){return e&&e.__esModule?e:{default:e}}var s={string:function(){return"string"},string_email:function(){return"user@example.com"},"string_date-time":function(){return(new Date).toISOString()},string_date:function(){return(new Date).toISOString().substring(0,10)},string_uuid:function(){return"3fa85f64-5717-4562-b3fc-2c963f66afa6"},string_hostname:function(){return"example.com"},string_ipv4:function(){return"198.51.100.42"},string_ipv6:function(){return"2001:0db8:5b96:0000:0000:426f:8e17:642a"},number:function(){return 0},number_float:function(){return 0},integer:function(){return 0},boolean:function(e){return"boolean"!=typeof e.default||e.default}},l=function(e){var t=e=(0,r.objectify)(e),n=t.type,o=t.format,i=s[n+"_"+o]||s[n];return(0,r.isFunc)(i)?i(e):"Unknown Type: "+e.type},c=t.sampleFromSchema=function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},o=(0,r.objectify)(t),i=o.type,a=o.example,u=o.properties,s=o.additionalProperties,c=o.items,f=n.includeReadOnly,p=n.includeWriteOnly;if(void 0!==a)return(0,r.deeplyStripKey)(a,"$$ref",function(e){return"string"==typeof e&&e.indexOf("#")>-1});if(!i)if(u)i="object";else{if(!c)return;i="array"}if("object"===i){var d=(0,r.objectify)(u),h={};for(var v in d)d[v]&&d[v].deprecated||d[v]&&d[v].readOnly&&!f||d[v]&&d[v].writeOnly&&!p||(h[v]=e(d[v],n));if(!0===s)h.additionalProp1={};else if(s)for(var m=(0,r.objectify)(s),g=e(m,n),y=1;y<4;y++)h["additionalProp"+y]=g;return h}return"array"===i?Array.isArray(c.anyOf)?c.anyOf.map(function(t){return e(t,n)}):Array.isArray(c.oneOf)?c.oneOf.map(function(t){return e(t,n)}):[e(c,n)]:t.enum?t.default?t.default:(0,r.normalizeArray)(t.enum)[0]:"file"!==i?l(t):void 0},f=(t.inferSchema=function(e){return e.schema&&(e=e.schema),e.properties&&(e.type="object"),e},t.sampleXmlFromSchema=function e(t){var n,o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=(0,a.default)({},(0,r.objectify)(t)),u=i.type,s=i.properties,c=i.additionalProperties,f=i.items,p=i.example,d=o.includeReadOnly,h=o.includeWriteOnly,v=i.default,m={},g={},y=t.xml,b=y.name,_=y.prefix,w=y.namespace,E=i.enum,x=void 0;if(!u)if(s||c)u="object";else{if(!f)return;u="array"}(b=b||"notagname",n=(_?_+":":"")+b,w)&&(g[_?"xmlns:"+_:"xmlns"]=w);if("array"===u&&f){if(f.xml=f.xml||y||{},f.xml.name=f.xml.name||y.name,y.wrapped)return m[n]=[],Array.isArray(p)?p.forEach(function(t){f.example=t,m[n].push(e(f,o))}):Array.isArray(v)?v.forEach(function(t){f.default=t,m[n].push(e(f,o))}):m[n]=[e(f,o)],g&&m[n].push({_attr:g}),m;var S=[];return Array.isArray(p)?(p.forEach(function(t){f.example=t,S.push(e(f,o))}),S):Array.isArray(v)?(v.forEach(function(t){f.default=t,S.push(e(f,o))}),S):e(f,o)}if("object"===u){var C=(0,r.objectify)(s);for(var k in m[n]=[],p=p||{},C)if(C.hasOwnProperty(k)&&(!C[k].readOnly||d)&&(!C[k].writeOnly||h))if(C[k].xml=C[k].xml||{},C[k].xml.attribute){var A=Array.isArray(C[k].enum)&&C[k].enum[0],O=C[k].example,P=C[k].default;g[C[k].xml.name||k]=void 0!==O&&O||void 0!==p[k]&&p[k]||void 0!==P&&P||A||l(C[k])}else{C[k].xml.name=C[k].xml.name||k,void 0===C[k].example&&void 0!==p[k]&&(C[k].example=p[k]);var T=e(C[k]);Array.isArray(T)?m[n]=m[n].concat(T):m[n].push(T)}return!0===c?m[n].push({additionalProp:"Anything can be here"}):c&&m[n].push({additionalProp:l(c)}),g&&m[n].push({_attr:g}),m}return x=void 0!==p?p:void 0!==v?v:Array.isArray(E)?E[0]:l(t),m[n]=g?[{_attr:g},x]:x,m});function p(e,t){var n=f(e,t);if(n)return(0,o.default)(n,{declaration:!0,indent:"\t"})}t.memoizedCreateXMLExample=(0,i.default)(p),t.memoizedSampleFromSchema=(0,i.default)(c)},function(e,t){function n(){this._events=this._events||{},this._maxListeners=this._maxListeners||void 0}function r(e){return"function"==typeof e}function o(e){return"object"==typeof e&&null!==e}function i(e){return void 0===e}e.exports=n,n.EventEmitter=n,n.prototype._events=void 0,n.prototype._maxListeners=void 0,n.defaultMaxListeners=10,n.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,n,a,u,s,l;if(this._events||(this._events={}),"error"===e&&(!this._events.error||o(this._events.error)&&!this._events.error.length)){if((t=arguments[1])instanceof Error)throw t;var c=new Error('Uncaught, unspecified "error" event. ('+t+")");throw c.context=t,c}if(i(n=this._events[e]))return!1;if(r(n))switch(arguments.length){case 1:n.call(this);break;case 2:n.call(this,arguments[1]);break;case 3:n.call(this,arguments[1],arguments[2]);break;default:u=Array.prototype.slice.call(arguments,1),n.apply(this,u)}else if(o(n))for(u=Array.prototype.slice.call(arguments,1),a=(l=n.slice()).length,s=0;s0&&this._events[e].length>a&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace()),this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){if(!r(t))throw TypeError("listener must be a function");var n=!1;function o(){this.removeListener(e,o),n||(n=!0,t.apply(this,arguments))}return o.listener=t,this.on(e,o),this},n.prototype.removeListener=function(e,t){var n,i,a,u;if(!r(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(a=(n=this._events[e]).length,i=-1,n===t||r(n.listener)&&n.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(n)){for(u=a;u-- >0;)if(n[u]===t||n[u].listener&&n[u].listener===t){i=u;break}if(i<0)return this;1===n.length?(n.length=0,delete this._events[e]):n.splice(i,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,n;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(r(n=this._events[e]))this.removeListener(e,n);else if(n)for(;n.length;)this.removeListener(e,n[n.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){return this._events&&this._events[e]?r(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.prototype.listenerCount=function(e){if(this._events){var t=this._events[e];if(r(t))return 1;if(t)return t.length}return 0},n.listenerCount=function(e,t){return e.listenerCount(t)}},function(e,t,n){(t=e.exports=n(306)).Stream=t,t.Readable=t,t.Writable=n(197),t.Duplex=n(65),t.Transform=n(311),t.PassThrough=n(665)},function(e,t,n){"use strict";(function(t,r,o){var i=n(140);function a(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var o=r.callback;t.pendingcb--,o(n),r=r.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=y;var u,s=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?r:i.nextTick;y.WritableState=g;var l=n(106);l.inherits=n(81);var c={deprecate:n(664)},f=n(307),p=n(141).Buffer,d=o.Uint8Array||function(){};var h,v=n(308);function m(){}function g(e,t){u=u||n(65),e=e||{};var r=t instanceof u;this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var o=e.highWaterMark,l=e.writableHighWaterMark,c=this.objectMode?16:16384;this.highWaterMark=o||0===o?o:r&&(l||0===l)?l:c,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var f=!1===e.decodeStrings;this.decodeStrings=!f,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,r=n.sync,o=n.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,r,o){--t.pendingcb,n?(i.nextTick(o,r),i.nextTick(S,e,t),e._writableState.errorEmitted=!0,e.emit("error",r)):(o(r),e._writableState.errorEmitted=!0,e.emit("error",r),S(e,t))}(e,n,r,t,o);else{var a=E(n);a||n.corked||n.bufferProcessing||!n.bufferedRequest||w(e,n),r?s(_,e,n,a,o):_(e,n,a,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new a(this)}function y(e){if(u=u||n(65),!(h.call(y,this)||this instanceof u))return new y(e);this._writableState=new g(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),f.call(this)}function b(e,t,n,r,o,i,a){t.writelen=r,t.writecb=a,t.writing=!0,t.sync=!0,n?e._writev(o,t.onwrite):e._write(o,i,t.onwrite),t.sync=!1}function _(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),S(e,t)}function w(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),i=t.corkedRequestsFree;i.entry=n;for(var u=0,s=!0;n;)o[u]=n,n.isBuf||(s=!1),n=n.next,u+=1;o.allBuffers=s,b(e,t,!0,t.length,o,"",i.finish),t.pendingcb++,t.lastBufferedRequest=null,i.next?(t.corkedRequestsFree=i.next,i.next=null):t.corkedRequestsFree=new a(t),t.bufferedRequestCount=0}else{for(;n;){var l=n.chunk,c=n.encoding,f=n.callback;if(b(e,t,!1,t.objectMode?1:l.length,l,c,f),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function E(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function x(e,t){e._final(function(n){t.pendingcb--,n&&e.emit("error",n),t.prefinished=!0,e.emit("prefinish"),S(e,t)})}function S(e,t){var n=E(t);return n&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,i.nextTick(x,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),n}l.inherits(y,f),g.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(g.prototype,"buffer",{get:c.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(h=Function.prototype[Symbol.hasInstance],Object.defineProperty(y,Symbol.hasInstance,{value:function(e){return!!h.call(this,e)||this===y&&(e&&e._writableState instanceof g)}})):h=function(e){return e instanceof this},y.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},y.prototype.write=function(e,t,n){var r,o=this._writableState,a=!1,u=!o.objectMode&&(r=e,p.isBuffer(r)||r instanceof d);return u&&!p.isBuffer(e)&&(e=function(e){return p.from(e)}(e)),"function"==typeof t&&(n=t,t=null),u?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=m),o.ended?function(e,t){var n=new Error("write after end");e.emit("error",n),i.nextTick(t,n)}(this,n):(u||function(e,t,n,r){var o=!0,a=!1;return null===n?a=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),i.nextTick(r,a),o=!1),o}(this,o,e,n))&&(o.pendingcb++,a=function(e,t,n,r,o,i){if(!n){var a=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=p.from(t,n));return t}(t,r,o);r!==a&&(n=!0,o="buffer",r=a)}var u=t.objectMode?1:r.length;t.length+=u;var s=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(y.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),y.prototype._write=function(e,t,n){n(new Error("_write() is not implemented"))},y.prototype._writev=null,y.prototype.end=function(e,t,n){var r=this._writableState;"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!==e&&void 0!==e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||r.finished||function(e,t,n){t.ending=!0,S(e,t),n&&(t.finished?i.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,r,n)},Object.defineProperty(y.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),y.prototype.destroy=v.destroy,y.prototype._undestroy=v.undestroy,y.prototype._destroy=function(e,t){this.end(),t(e)}}).call(t,n(56),n(309).setImmediate,n(31))},function(e,t,n){"use strict";e.exports=function(e){return"function"==typeof e}},function(e,t,n){"use strict";e.exports=n(691)()?Array.from:n(692)},function(e,t,n){"use strict";var r=n(705),o=n(67),i=n(82),a=Array.prototype.indexOf,u=Object.prototype.hasOwnProperty,s=Math.abs,l=Math.floor;e.exports=function(e){var t,n,c,f;if(!r(e))return a.apply(this,arguments);for(n=o(i(this).length),c=arguments[1],t=c=isNaN(c)?0:c>=0?l(c):o(this.length)-l(s(c));t1&&void 0!==arguments[1])||arguments[1];return e=(0,r.normalizeArray)(e),{type:u,payload:{thing:e,shown:t}}},t.changeMode=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=(0,r.normalizeArray)(e),{type:a,payload:{thing:e,mode:t}}};var r=n(9),o=t.UPDATE_LAYOUT="layout_update_layout",i=t.UPDATE_FILTER="layout_update_filter",a=t.UPDATE_MODE="layout_update_mode",u=t.SHOW="layout_show"},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.execute=t.executeRequest=t.logRequest=t.setMutatedRequest=t.setRequest=t.setResponse=t.updateEmptyParamInclusion=t.validateParams=t.invalidateResolvedSubtreeCache=t.updateResolvedSubtree=t.requestResolvedSubtree=t.resolveSpec=t.parseToJson=t.SET_SCHEME=t.UPDATE_RESOLVED_SUBTREE=t.UPDATE_RESOLVED=t.UPDATE_OPERATION_META_VALUE=t.CLEAR_VALIDATE_PARAMS=t.CLEAR_REQUEST=t.CLEAR_RESPONSE=t.LOG_REQUEST=t.SET_MUTATED_REQUEST=t.SET_REQUEST=t.SET_RESPONSE=t.VALIDATE_PARAMS=t.UPDATE_EMPTY_PARAM_INCLUSION=t.UPDATE_PARAM=t.UPDATE_JSON=t.UPDATE_URL=t.UPDATE_SPEC=void 0;var r=b(n(25)),o=b(n(84)),i=b(n(23)),a=b(n(42)),u=b(n(204)),s=b(n(338)),l=b(n(339)),c=b(n(45));t.updateSpec=function(e){var t=L(e).replace(/\t/g," ");if("string"==typeof e)return{type:_,payload:t}},t.updateResolved=function(e){return{type:N,payload:e}},t.updateUrl=function(e){return{type:w,payload:e}},t.updateJsonSpec=function(e){return{type:E,payload:e}},t.changeParam=function(e,t,n,r,o){return{type:x,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}},t.changeParamByIdentity=function(e,t,n,r){return{type:x,payload:{path:e,param:t,value:n,isXml:r}}},t.clearValidateParams=function(e){return{type:I,payload:{pathMethod:e}}},t.changeConsumesValue=function(e,t){return{type:j,payload:{path:e,value:t,key:"consumes_value"}}},t.changeProducesValue=function(e,t){return{type:j,payload:{path:e,value:t,key:"produces_value"}}},t.clearResponse=function(e,t){return{type:T,payload:{path:e,method:t}}},t.clearRequest=function(e,t){return{type:M,payload:{path:e,method:t}}},t.setScheme=function(e,t,n){return{type:D,payload:{scheme:e,path:t,method:n}}};var f=b(n(208)),p=n(7),d=b(n(210)),h=b(n(180)),v=b(n(342)),m=b(n(764)),g=b(n(766)),y=n(9);function b(e){return e&&e.__esModule?e:{default:e}}var _=t.UPDATE_SPEC="spec_update_spec",w=t.UPDATE_URL="spec_update_url",E=t.UPDATE_JSON="spec_update_json",x=t.UPDATE_PARAM="spec_update_param",S=t.UPDATE_EMPTY_PARAM_INCLUSION="spec_update_empty_param_inclusion",C=t.VALIDATE_PARAMS="spec_validate_param",k=t.SET_RESPONSE="spec_set_response",A=t.SET_REQUEST="spec_set_request",O=t.SET_MUTATED_REQUEST="spec_set_mutated_request",P=t.LOG_REQUEST="spec_log_request",T=t.CLEAR_RESPONSE="spec_clear_response",M=t.CLEAR_REQUEST="spec_clear_request",I=t.CLEAR_VALIDATE_PARAMS="spec_clear_validate_param",j=t.UPDATE_OPERATION_META_VALUE="spec_update_operation_meta_value",N=t.UPDATE_RESOLVED="spec_update_resolved",R=t.UPDATE_RESOLVED_SUBTREE="spec_update_resolved_subtree",D=t.SET_SCHEME="set_scheme",L=function(e){return(0,v.default)(e)?e:""};t.parseToJson=function(e){return function(t){var n=t.specActions,r=t.specSelectors,o=t.errActions,i=r.specStr,a=null;try{e=e||i(),o.clear({source:"parser"}),a=f.default.safeLoad(e)}catch(e){return console.error(e),o.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return a&&"object"===(void 0===a?"undefined":(0,c.default)(a))?n.updateJsonSpec(a):{}}};var U=!1,q=(t.resolveSpec=function(e,t){return function(n){var r=n.specActions,o=n.specSelectors,i=n.errActions,a=n.fn,u=a.fetch,s=a.resolve,l=a.AST,c=void 0===l?{}:l,f=n.getConfigs;U||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),U=!0);var p=f(),d=p.modelPropertyMacro,h=p.parameterMacro,v=p.requestInterceptor,m=p.responseInterceptor;void 0===e&&(e=o.specJson()),void 0===t&&(t=o.url());var g=c.getLineNumberForPath?c.getLineNumberForPath:function(){},y=o.specStr();return s({fetch:u,spec:e,baseDoc:t,modelPropertyMacro:d,parameterMacro:h,requestInterceptor:v,responseInterceptor:m}).then(function(e){var t=e.spec,n=e.errors;if(i.clear({type:"thrown"}),Array.isArray(n)&&n.length>0){var o=n.map(function(e){return console.error(e),e.line=e.fullPath?g(y,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e});i.newThrownErrBatch(o)}return r.updateResolved(t)})}},[]),F=(0,m.default)((0,l.default)(s.default.mark(function e(){var t,n,r,o,i,a,c,f,d,h,v,m,y,b,_,w,E;return s.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(t=q.system){e.next=4;break}return console.error("debResolveSubtrees: don't have a system to operate on, aborting."),e.abrupt("return");case 4:if(n=t.errActions,r=t.errSelectors,o=t.fn,i=o.resolveSubtree,a=o.AST,c=void 0===a?{}:a,f=t.specSelectors,d=t.specActions,i){e.next=8;break}return console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing."),e.abrupt("return");case 8:return h=c.getLineNumberForPath?c.getLineNumberForPath:function(){},v=f.specStr(),m=t.getConfigs(),y=m.modelPropertyMacro,b=m.parameterMacro,_=m.requestInterceptor,w=m.responseInterceptor,e.prev=11,e.next=14,q.reduce(function(){var e=(0,l.default)(s.default.mark(function e(t,o){var a,u,l,c,p,d,m;return s.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,t;case 2:return a=e.sent,u=a.resultMap,l=a.specWithCurrentSubtrees,e.next=7,i(l,o,{baseDoc:f.url(),modelPropertyMacro:y,parameterMacro:b,requestInterceptor:_,responseInterceptor:w});case 7:return c=e.sent,p=c.errors,d=c.spec,r.allErrors().size&&n.clearBy(function(e){return"thrown"!==e.get("type")||"resolver"!==e.get("source")||!e.get("fullPath").every(function(e,t){return e===o[t]||void 0===o[t]})}),Array.isArray(p)&&p.length>0&&(m=p.map(function(e){return e.line=e.fullPath?h(v,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",Object.defineProperty(e,"message",{enumerable:!0,value:e.message}),e}),n.newThrownErrBatch(m)),(0,g.default)(u,o,d),(0,g.default)(l,o,d),e.abrupt("return",{resultMap:u,specWithCurrentSubtrees:l});case 15:case"end":return e.stop()}},e,void 0)}));return function(t,n){return e.apply(this,arguments)}}(),u.default.resolve({resultMap:(f.specResolvedSubtree([])||(0,p.Map)()).toJS(),specWithCurrentSubtrees:f.specJson().toJS()}));case 14:E=e.sent,delete q.system,q=[],e.next=22;break;case 19:e.prev=19,e.t0=e.catch(11),console.error(e.t0);case 22:d.updateResolvedSubtree([],E.resultMap);case 23:case"end":return e.stop()}},e,void 0,[[11,19]])})),35);t.requestResolvedSubtree=function(e){return function(t){q.map(function(e){return e.join("@@")}).indexOf(e.join("@@"))>-1||(q.push(e),q.system=t,F())}};t.updateResolvedSubtree=function(e,t){return{type:R,payload:{path:e,value:t}}},t.invalidateResolvedSubtreeCache=function(){return{type:R,payload:{path:[],value:(0,p.Map)()}}},t.validateParams=function(e,t){return{type:C,payload:{pathMethod:e,isOAS3:t}}},t.updateEmptyParamInclusion=function(e,t,n,r){return{type:S,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}}};t.setResponse=function(e,t,n){return{payload:{path:e,method:t,res:n},type:k}},t.setRequest=function(e,t,n){return{payload:{path:e,method:t,req:n},type:A}},t.setMutatedRequest=function(e,t,n){return{payload:{path:e,method:t,req:n},type:O}},t.logRequest=function(e){return{payload:e,type:P}},t.executeRequest=function(e){return function(t){var n=t.fn,r=t.specActions,o=t.specSelectors,u=t.getConfigs,s=t.oas3Selectors,l=e.pathName,c=e.method,f=e.operation,p=u(),v=p.requestInterceptor,m=p.responseInterceptor,g=f.toJS();if(f&&f.get("parameters")&&f.get("parameters").filter(function(e){return e&&!0===e.get("allowEmptyValue")}).forEach(function(t){if(o.parameterInclusionSettingFor([l,c],t.get("name"),t.get("in"))){e.parameters=e.parameters||{};var n=(0,y.paramToValue)(t,e.parameters);(!n||n&&0===n.size)&&(e.parameters[t.get("name")]="")}}),e.contextUrl=(0,d.default)(o.url()).toString(),g&&g.operationId?e.operationId=g.operationId:g&&l&&c&&(e.operationId=n.opId(g,l,c)),o.isOAS3()){var b=l+":"+c;e.server=s.selectedServer(b)||s.selectedServer();var _=s.serverVariables({server:e.server,namespace:b}).toJS(),w=s.serverVariables({server:e.server}).toJS();e.serverVariables=(0,a.default)(_).length?_:w,e.requestContentType=s.requestContentType(l,c),e.responseContentType=s.responseContentType(l,c)||"*/*";var E=s.requestBodyValue(l,c);(0,y.isJSONObject)(E)?e.requestBody=JSON.parse(E):E&&E.toJS?e.requestBody=E.toJS():e.requestBody=E}var x=(0,i.default)({},e);x=n.buildRequest(x),r.setRequest(e.pathName,e.method,x);e.requestInterceptor=function(t){var n=v.apply(this,[t]),o=(0,i.default)({},n);return r.setMutatedRequest(e.pathName,e.method,o),n},e.responseInterceptor=m;var S=Date.now();return n.execute(e).then(function(t){t.duration=Date.now()-S,r.setResponse(e.pathName,e.method,t)}).catch(function(t){return r.setResponse(e.pathName,e.method,{error:!0,err:(0,h.default)(t)})})}};t.execute=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.path,n=e.method,i=(0,o.default)(e,["path","method"]);return function(e){var o=e.fn.fetch,a=e.specSelectors,u=e.specActions,s=a.specJsonWithResolvedSubtrees().toJS(),l=a.operationScheme(t,n),c=a.contentTypeValues([t,n]).toJS(),f=c.requestContentType,p=c.responseContentType,d=/xml/i.test(f),h=a.parameterValues([t,n],d).toJS();return u.executeRequest((0,r.default)({},i,{fetch:o,spec:s,pathName:t,method:n,parameters:h,requestContentType:f,scheme:l,responseContentType:p}))}}},function(e,t,n){e.exports={default:n(733),__esModule:!0}},function(e,t){e.exports=function(e,t,n,r){if(!(e instanceof t)||void 0!==r&&r in e)throw TypeError(n+": incorrect invocation!");return e}},function(e,t,n){"use strict";var r=n(94);e.exports.f=function(e){return new function(e){var t,n;this.promise=new e(function(e,r){if(void 0!==t||void 0!==n)throw TypeError("Bad Promise constructor");t=e,n=r}),this.resolve=r(t),this.reject=r(n)}(e)}},function(e,t,n){var r=n(50);e.exports=function(e,t,n){for(var o in t)n&&e[o]?e[o]=t[o]:r(e,o,t[o]);return e}},function(e,t,n){"use strict";var r=n(742);e.exports=r},function(e,t,n){"use strict";var r=n(86);e.exports=new r({explicit:[n(745),n(746),n(747)]})},function(e,t,n){"use strict";(function(t){var r=n(762),o=n(763),i=/^([a-z][a-z0-9.+-]*:)?(\/\/)?([\S\s]*)/i,a=/^[A-Za-z][A-Za-z0-9+-.]*:\/\//,u=[["#","hash"],["?","query"],function(e){return e.replace("\\","/")},["/","pathname"],["@","auth",1],[NaN,"host",void 0,1,1],[/:(\d+)$/,"port",void 0,1],[NaN,"hostname",void 0,1,1]],s={hash:1,query:1};function l(e){var n,r=t&&t.location||{},o={},i=typeof(e=e||r);if("blob:"===e.protocol)o=new f(unescape(e.pathname),{});else if("string"===i)for(n in o=new f(e,{}),s)delete o[n];else if("object"===i){for(n in e)n in s||(o[n]=e[n]);void 0===o.slashes&&(o.slashes=a.test(e.href))}return o}function c(e){var t=i.exec(e);return{protocol:t[1]?t[1].toLowerCase():"",slashes:!!t[2],rest:t[3]}}function f(e,t,n){if(!(this instanceof f))return new f(e,t,n);var i,a,s,p,d,h,v=u.slice(),m=typeof t,g=this,y=0;for("object"!==m&&"string"!==m&&(n=t,t=null),n&&"function"!=typeof n&&(n=o.parse),t=l(t),i=!(a=c(e||"")).protocol&&!a.slashes,g.slashes=a.slashes||i&&t.slashes,g.protocol=a.protocol||t.protocol||"",e=a.rest,a.slashes||(v[3]=[/(.*)/,"pathname"]);y-1||r("96",e),!l.plugins[n]){t.extractEvents||r("97",e),l.plugins[n]=t;var a=t.eventTypes;for(var s in a)u(a[s],t,s)||r("98",s,e)}}}function u(e,t,n){l.eventNameDispatchConfigs.hasOwnProperty(n)&&r("99",n),l.eventNameDispatchConfigs[n]=e;var o=e.phasedRegistrationNames;if(o){for(var i in o){if(o.hasOwnProperty(i))s(o[i],t,n)}return!0}return!!e.registrationName&&(s(e.registrationName,t,n),!0)}function s(e,t,n){l.registrationNameModules[e]&&r("100",e),l.registrationNameModules[e]=t,l.registrationNameDependencies[e]=t.eventTypes[n].dependencies}var l={plugins:[],eventNameDispatchConfigs:{},registrationNameModules:{},registrationNameDependencies:{},possibleRegistrationNames:null,injectEventPluginOrder:function(e){o&&r("101"),o=Array.prototype.slice.call(e),a()},injectEventPluginsByName:function(e){var t=!1;for(var n in e)if(e.hasOwnProperty(n)){var o=e[n];i.hasOwnProperty(n)&&i[n]===o||(i[n]&&r("102",n),i[n]=o,t=!0)}t&&a()},getPluginModuleForEvent:function(e){var t=e.dispatchConfig;if(t.registrationName)return l.registrationNameModules[t.registrationName]||null;if(void 0!==t.phasedRegistrationNames){var n=t.phasedRegistrationNames;for(var r in n)if(n.hasOwnProperty(r)){var o=l.registrationNameModules[n[r]];if(o)return o}}return null},_resetEventPlugins:function(){for(var e in o=null,i)i.hasOwnProperty(e)&&delete i[e];l.plugins.length=0;var t=l.eventNameDispatchConfigs;for(var n in t)t.hasOwnProperty(n)&&delete t[n];var r=l.registrationNameModules;for(var a in r)r.hasOwnProperty(a)&&delete r[a]}};e.exports=l},function(e,t,n){"use strict";var r,o,i=n(11),a=n(213);n(8),n(10);function u(e,t,n,r){var o=e.type||"unknown-event";e.currentTarget=s.getNodeFromInstance(r),t?a.invokeGuardedCallbackWithCatch(o,n,e):a.invokeGuardedCallback(o,n,e),e.currentTarget=null}var s={isEndish:function(e){return"topMouseUp"===e||"topTouchEnd"===e||"topTouchCancel"===e},isMoveish:function(e){return"topMouseMove"===e||"topTouchMove"===e},isStartish:function(e){return"topMouseDown"===e||"topTouchStart"===e},executeDirectDispatch:function(e){var t=e._dispatchListeners,n=e._dispatchInstances;Array.isArray(t)&&i("103"),e.currentTarget=t?s.getNodeFromInstance(n):null;var r=t?t(e):null;return e.currentTarget=null,e._dispatchListeners=null,e._dispatchInstances=null,r},executeDispatchesInOrder:function(e,t){var n=e._dispatchListeners,r=e._dispatchInstances;if(Array.isArray(n))for(var o=0;o0&&r.length<20?n+" (keys: "+r.join(", ")+")":n}function s(e,t){var n=o.get(e);return n||null}var l={isMounted:function(e){var t=o.get(e);return!!t&&!!t._renderedComponent},enqueueCallback:function(e,t,n){l.validateCallback(t,n);var r=s(e);if(!r)return null;r._pendingCallbacks?r._pendingCallbacks.push(t):r._pendingCallbacks=[t],a(r)},enqueueCallbackInternal:function(e,t){e._pendingCallbacks?e._pendingCallbacks.push(t):e._pendingCallbacks=[t],a(e)},enqueueForceUpdate:function(e){var t=s(e);t&&(t._pendingForceUpdate=!0,a(t))},enqueueReplaceState:function(e,t,n){var r=s(e);r&&(r._pendingStateQueue=[t],r._pendingReplaceState=!0,void 0!==n&&null!==n&&(l.validateCallback(n,"replaceState"),r._pendingCallbacks?r._pendingCallbacks.push(n):r._pendingCallbacks=[n]),a(r))},enqueueSetState:function(e,t){var n=s(e);n&&((n._pendingStateQueue||(n._pendingStateQueue=[])).push(t),a(n))},enqueueElementInternal:function(e,t,n){e._pendingElement=t,e._context=n,a(e)},validateCallback:function(e,t){e&&"function"!=typeof e&&r("122",t,u(e))}};e.exports=l},function(e,t,n){"use strict";n(13);var r=n(34),o=(n(10),r);e.exports=o},function(e,t,n){"use strict";e.exports=function(e){var t,n=e.keyCode;return"charCode"in e?0===(t=e.charCode)&&13===n&&(t=13):t=n,t>=32||13===t?t:0}},function(e,t,n){var r=n(62),o=n(229),i=n(47),a="[object Object]",u=Function.prototype,s=Object.prototype,l=u.toString,c=s.hasOwnProperty,f=l.call(Object);e.exports=function(e){if(!i(e)||r(e)!=a)return!1;var t=o(e);if(null===t)return!0;var n=c.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&l.call(n)==f}},function(e,t,n){var r=n(298)(Object.getPrototypeOf,Object);e.exports=r},function(e,t,n){var r=n(292);e.exports=function(e){var t=new e.constructor(e.byteLength);return new r(t).set(new r(e)),t}},function(e,t){var n=this&&this.__extends||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);function r(){this.constructor=e}e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)},r=Object.prototype.hasOwnProperty; +/*! + * https://github.com/Starcounter-Jack/JSON-Patch + * (c) 2017 Joachim Wester + * MIT license + */function o(e,t){return r.call(e,t)}function i(e){if(Array.isArray(e)){for(var t=new Array(e.length),n=0;n=48&&t<=57))return!1;n++}return!0},t.escapePathComponent=a,t.unescapePathComponent=function(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")},t._getPathRecursive=u,t.getPath=function(e,t){if(e===t)return"/";var n=u(e,t);if(""===n)throw new Error("Object not found in root");return"/"+n},t.hasUndefined=function e(t){if(void 0===t)return!0;if(t)if(Array.isArray(t)){for(var n=0,r=t.length;nw;w++)if((p||w in y)&&(m=b(v=y[w],w,g),e))if(n)E[w]=m;else if(m)switch(e){case 3:return!0;case 5:return v;case 6:return w;case 2:E.push(v)}else if(c)return!1;return f?-1:l||c?c:E}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.authorizeRequest=t.authorizeAccessCodeWithBasicAuthentication=t.authorizeAccessCodeWithFormParams=t.authorizeApplication=t.authorizePassword=t.preAuthorizeImplicit=t.CONFIGURE_AUTH=t.VALIDATE=t.AUTHORIZE_OAUTH2=t.PRE_AUTHORIZE_OAUTH2=t.LOGOUT=t.AUTHORIZE=t.SHOW_AUTH_POPUP=void 0;var r=l(n(45)),o=l(n(23)),i=l(n(41));t.showDefinitions=function(e){return{type:c,payload:e}},t.authorize=function(e){return{type:f,payload:e}},t.logout=function(e){return{type:p,payload:e}},t.authorizeOauth2=function(e){return{type:d,payload:e}},t.configureAuth=function(e){return{type:h,payload:e}};var a=l(n(210)),u=l(n(32)),s=n(9);function l(e){return e&&e.__esModule?e:{default:e}}var c=t.SHOW_AUTH_POPUP="show_popup",f=t.AUTHORIZE="authorize",p=t.LOGOUT="logout",d=(t.PRE_AUTHORIZE_OAUTH2="pre_authorize_oauth2",t.AUTHORIZE_OAUTH2="authorize_oauth2"),h=(t.VALIDATE="validate",t.CONFIGURE_AUTH="configure_auth");t.preAuthorizeImplicit=function(e){return function(t){var n=t.authActions,r=t.errActions,o=e.auth,a=e.token,s=e.isValid,l=o.schema,c=o.name,f=l.get("flow");delete u.default.swaggerUIRedirectOauth2,"accessCode"===f||s||r.newAuthErr({authId:c,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),a.error?r.newAuthErr({authId:c,source:"auth",level:"error",message:(0,i.default)(a)}):n.authorizeOauth2({auth:o,token:a})}};t.authorizePassword=function(e){return function(t){var n=t.authActions,r=e.schema,i=e.name,a=e.username,u=e.password,l=e.passwordType,c=e.clientId,f=e.clientSecret,p={grant_type:"password",scope:e.scopes.join(" "),username:a,password:u},d={};switch(l){case"request-body":!function(e,t,n){t&&(0,o.default)(e,{client_id:t});n&&(0,o.default)(e,{client_secret:n})}(p,c,f);break;case"basic":d.Authorization="Basic "+(0,s.btoa)(c+":"+f);break;default:console.warn("Warning: invalid passwordType "+l+" was passed, not including client id and secret")}return n.authorizeRequest({body:(0,s.buildFormData)(p),url:r.get("tokenUrl"),name:i,headers:d,query:{},auth:e})}};t.authorizeApplication=function(e){return function(t){var n=t.authActions,r=e.schema,o=e.scopes,i=e.name,a=e.clientId,u=e.clientSecret,l={Authorization:"Basic "+(0,s.btoa)(a+":"+u)},c={grant_type:"client_credentials",scope:o.join(" ")};return n.authorizeRequest({body:(0,s.buildFormData)(c),name:i,url:r.get("tokenUrl"),auth:e,headers:l})}},t.authorizeAccessCodeWithFormParams=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,u=t.clientSecret,l={grant_type:"authorization_code",code:t.code,client_id:a,client_secret:u,redirect_uri:n};return r.authorizeRequest({body:(0,s.buildFormData)(l),name:i,url:o.get("tokenUrl"),auth:t})}},t.authorizeAccessCodeWithBasicAuthentication=function(e){var t=e.auth,n=e.redirectUrl;return function(e){var r=e.authActions,o=t.schema,i=t.name,a=t.clientId,u=t.clientSecret,l={Authorization:"Basic "+(0,s.btoa)(a+":"+u)},c={grant_type:"authorization_code",code:t.code,client_id:a,redirect_uri:n};return r.authorizeRequest({body:(0,s.buildFormData)(c),name:i,url:o.get("tokenUrl"),auth:t,headers:l})}},t.authorizeRequest=function(e){return function(t){var n=t.fn,u=t.getConfigs,s=t.authActions,l=t.errActions,c=t.oas3Selectors,f=t.specSelectors,p=t.authSelectors,d=e.body,h=e.query,v=void 0===h?{}:h,m=e.headers,g=void 0===m?{}:m,y=e.name,b=e.url,_=e.auth,w=(p.getConfigs()||{}).additionalQueryStringParams,E=void 0;E=f.isOAS3()?(0,a.default)(b,c.selectedServer(),!0):(0,a.default)(b,f.url(),!0),"object"===(void 0===w?"undefined":(0,r.default)(w))&&(E.query=(0,o.default)({},E.query,w));var x=E.toString(),S=(0,o.default)({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"},g);n.fetch({url:x,method:"post",headers:S,query:v,body:d,requestInterceptor:u().requestInterceptor,responseInterceptor:u().responseInterceptor}).then(function(e){var t=JSON.parse(e.data),n=t&&(t.error||""),r=t&&(t.parseError||"");e.ok?n||r?l.newAuthErr({authId:y,level:"error",source:"auth",message:(0,i.default)(t)}):s.authorizeOauth2({auth:_,token:t}):l.newAuthErr({authId:y,level:"error",source:"auth",message:e.statusText})}).catch(function(e){var t=new Error(e).message;if(e.response&&e.response.data){var n=e.response.data;try{var r="string"==typeof n?JSON.parse(n):n;r.error&&(t+=", error: "+r.error),r.error_description&&(t+=", description: "+r.error_description)}catch(e){}}l.newAuthErr({authId:y,level:"error",source:"auth",message:t})})}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.parseYamlConfig=void 0;var r,o=n(208),i=(r=o)&&r.__esModule?r:{default:r};t.parseYamlConfig=function(e,t){try{return i.default.safeLoad(e)}catch(e){return t&&t.errActions.newThrownErr(new Error(e)),{}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.loaded=t.TOGGLE_CONFIGS=t.UPDATE_CONFIGS=void 0;var r,o=n(22),i=(r=o)&&r.__esModule?r:{default:r};t.update=function(e,t){return{type:a,payload:(0,i.default)({},e,t)}},t.toggle=function(e){return{type:u,payload:e}};var a=t.UPDATE_CONFIGS="configs_update",u=t.TOGGLE_CONFIGS="configs_toggle";t.loaded=function(){return function(){}}},function(e,t,n){"use strict";function r(e,t,n,r,o){this.src=e,this.env=r,this.options=n,this.parser=t,this.tokens=o,this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache=[],this.isInLabel=!1,this.linkLevel=0,this.linkContent="",this.labelUnmatchedScopes=0}r.prototype.pushPending=function(){this.tokens.push({type:"text",content:this.pending,level:this.pendingLevel}),this.pending=""},r.prototype.push=function(e){this.pending&&this.pushPending(),this.tokens.push(e),this.pendingLevel=this.level},r.prototype.cacheSet=function(e,t){for(var n=this.cache.length;n<=e;n++)this.cache.push(0);this.cache[e]=t},r.prototype.cacheGet=function(e){return es;)r(u,n=t[s++])&&(~i(l,n)||l.push(n));return l}},function(e,t,n){var r=n(21).document;e.exports=r&&r.documentElement},function(e,t,n){var r=n(52),o=n(72),i=n(162)("IE_PROTO"),a=Object.prototype;e.exports=Object.getPrototypeOf||function(e){return e=o(e),r(e,i)?e[i]:"function"==typeof e.constructor&&e instanceof e.constructor?e.constructor.prototype:e instanceof Object?a:null}},function(e,t,n){var r=n(33),o=r["__core-js_shared__"]||(r["__core-js_shared__"]={});e.exports=function(e){return o[e]||(o[e]={})}},function(e,t){e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},function(e,t,n){"use strict";var r=n(246)(!0);n(247)(String,"String",function(e){this._t=String(e),this._i=0},function(){var e,t=this._t,n=this._i;return n>=t.length?{value:void 0,done:!0}:(e=r(t,n),this._i+=e.length,{value:e,done:!1})})},function(e,t,n){var r=n(119),o=n(53);e.exports=function(e){return function(t,n){var i,a,u=String(o(t)),s=r(n),l=u.length;return s<0||s>=l?e?"":void 0:(i=u.charCodeAt(s))<55296||i>56319||s+1===l||(a=u.charCodeAt(s+1))<56320||a>57343?e?u.charAt(s):i:e?u.slice(s,s+2):a-56320+(i-55296<<10)+65536}}},function(e,t,n){"use strict";var r=n(248),o=n(29),i=n(73),a=n(59),u=n(102),s=n(462),l=n(171),c=n(468),f=n(18)("iterator"),p=!([].keys&&"next"in[].keys()),d=function(){return this};e.exports=function(e,t,n,h,v,m,g){s(n,t,h);var y,b,_,w=function(e){if(!p&&e in C)return C[e];switch(e){case"keys":case"values":return function(){return new n(this,e)}}return function(){return new n(this,e)}},E=t+" Iterator",x="values"==v,S=!1,C=e.prototype,k=C[f]||C["@@iterator"]||v&&C[v],A=k||w(v),O=v?x?w("entries"):A:void 0,P="Array"==t&&C.entries||k;if(P&&(_=c(P.call(new e)))!==Object.prototype&&_.next&&(l(_,E,!0),r||"function"==typeof _[f]||a(_,f,d)),x&&k&&"values"!==k.name&&(S=!0,A=function(){return k.call(this)}),r&&!g||!p&&!S&&C[f]||a(C,f,A),u[t]=A,u[E]=d,v)if(y={values:x?A:w("values"),keys:m?A:w("keys"),entries:O},g)for(b in y)b in C||i(C,b,y[b]);else o(o.P+o.F*(p||S),t,y);return y}},function(e,t){e.exports=!1},function(e,t,n){var r=n(465),o=n(251);e.exports=Object.keys||function(e){return r(e,o)}},function(e,t,n){var r=n(119),o=Math.max,i=Math.min;e.exports=function(e,t){return(e=r(e))<0?o(e+t,0):i(e,t)}},function(e,t){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},function(e,t,n){var r=n(33).document;e.exports=r&&r.documentElement},function(e,t,n){var r=n(60),o=n(121),i=n(18)("species");e.exports=function(e,t){var n,a=r(e).constructor;return void 0===a||void 0==(n=r(a)[i])?t:o(n)}},function(e,t,n){var r,o,i,a=n(120),u=n(480),s=n(252),l=n(169),c=n(33),f=c.process,p=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,v=c.Dispatch,m=0,g={},y=function(){var e=+this;if(g.hasOwnProperty(e)){var t=g[e];delete g[e],t()}},b=function(e){y.call(e.data)};p&&d||(p=function(e){for(var t=[],n=1;arguments.length>n;)t.push(arguments[n++]);return g[++m]=function(){u("function"==typeof e?e:Function(e),t)},r(m),m},d=function(e){delete g[e]},"process"==n(99)(f)?r=function(e){f.nextTick(a(y,e,1))}:v&&v.now?r=function(e){v.now(a(y,e,1))}:h?(i=(o=new h).port2,o.port1.onmessage=b,r=a(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(r=function(e){c.postMessage(e+"","*")},c.addEventListener("message",b,!1)):r="onreadystatechange"in l("script")?function(e){s.appendChild(l("script")).onreadystatechange=function(){s.removeChild(this),y.call(e)}}:function(e){setTimeout(a(y,e,1),0)}),e.exports={set:p,clear:d}},function(e,t){e.exports=function(e){try{return{e:!1,v:e()}}catch(e){return{e:!0,v:e}}}},function(e,t,n){var r=n(60),o=n(74),i=n(172);e.exports=function(e,t){if(r(e),o(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){var r=n(74),o=n(99),i=n(18)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[i])?!!t:"RegExp"==o(e))}},function(e,t,n){var r=n(20),o=n(15),i=n(51);e.exports=function(e,t){var n=(o.Object||{})[e]||Object[e],a={};a[e]=t(n),r(r.S+r.F*i(function(){n(1)}),"Object",a)}},function(e,t,n){var r=n(93);e.exports=Array.isArray||function(e){return"Array"==r(e)}},function(e,t,n){var r=n(240),o=n(164).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return r(e,o)}},function(e,t,n){var r=n(125),o=n(95),i=n(71),a=n(158),u=n(52),s=n(239),l=Object.getOwnPropertyDescriptor;t.f=n(44)?l:function(e,t){if(e=i(e),t=a(t,!0),s)try{return l(e,t)}catch(e){}if(u(e,t))return o(!r.f.call(e,t),e[t])}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t,n){e.exports={default:n(532),__esModule:!0}},function(e,t,n){"use strict";var r=n(96),o=n(177),i=n(125),a=n(72),u=n(155),s=Object.assign;e.exports=!s||n(51)(function(){var e={},t={},n=Symbol(),r="abcdefghijklmnopqrst";return e[n]=7,r.split("").forEach(function(e){t[e]=e}),7!=s({},e)[n]||Object.keys(s({},t)).join("")!=r})?function(e,t){for(var n=a(e),s=arguments.length,l=1,c=o.f,f=i.f;s>l;)for(var p,d=u(arguments[l++]),h=c?r(d).concat(c(d)):r(d),v=h.length,m=0;v>m;)f.call(d,p=h[m++])&&(n[p]=d[p]);return n}:s},function(e,t,n){"use strict";var r=n(104),o=n(13),i=n(266),a=(n(267),n(126));n(8),n(536);function u(e,t,n){this.props=e,this.context=t,this.refs=a,this.updater=n||i}function s(e,t,n){this.props=e,this.context=t,this.refs=a,this.updater=n||i}function l(){}u.prototype.isReactComponent={},u.prototype.setState=function(e,t){"object"!=typeof e&&"function"!=typeof e&&null!=e&&r("85"),this.updater.enqueueSetState(this,e),t&&this.updater.enqueueCallback(this,t,"setState")},u.prototype.forceUpdate=function(e){this.updater.enqueueForceUpdate(this),e&&this.updater.enqueueCallback(this,e,"forceUpdate")},l.prototype=u.prototype,s.prototype=new l,s.prototype.constructor=s,o(s.prototype,u.prototype),s.prototype.isPureReactComponent=!0,e.exports={Component:u,PureComponent:s}},function(e,t,n){"use strict";n(10);var r={isMounted:function(e){return!1},enqueueCallback:function(e,t){},enqueueForceUpdate:function(e){},enqueueReplaceState:function(e,t){},enqueueSetState:function(e,t){}};e.exports=r},function(e,t,n){"use strict";var r=!1;e.exports=r},function(e,t,n){"use strict";var r="function"==typeof Symbol&&Symbol.for&&Symbol.for("react.element")||60103;e.exports=r},function(e,t,n){"use strict";var r=n(544);e.exports=function(e){return r(e,!1)}},function(e,t,n){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(272),o=n(561),i=n(562),a=n(563),u=n(276);n(275);n.d(t,"createStore",function(){return r.b}),n.d(t,"combineReducers",function(){return o.a}),n.d(t,"bindActionCreators",function(){return i.a}),n.d(t,"applyMiddleware",function(){return a.a}),n.d(t,"compose",function(){return u.a})},function(e,t,n){"use strict";n.d(t,"a",function(){return i}),t.b=function e(t,n,a){var u;"function"==typeof n&&void 0===a&&(a=n,n=void 0);if(void 0!==a){if("function"!=typeof a)throw new Error("Expected the enhancer to be a function.");return a(e)(t,n)}if("function"!=typeof t)throw new Error("Expected the reducer to be a function.");var s=t;var l=n;var c=[];var f=c;var p=!1;function d(){f===c&&(f=c.slice())}function h(){return l}function v(e){if("function"!=typeof e)throw new Error("Expected listener to be a function.");var t=!0;return d(),f.push(e),function(){if(t){t=!1,d();var n=f.indexOf(e);f.splice(n,1)}}}function m(e){if(!r.a(e))throw new Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===e.type)throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(p)throw new Error("Reducers may not dispatch actions.");try{p=!0,l=s(l,e)}finally{p=!1}for(var t=c=f,n=0;no?0:o+t),(n=n>o?o:n)<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var i=Array(o);++rp))return!1;var h=c.get(e);if(h&&c.get(t))return h==t;var v=-1,m=!0,g=n&u?new r:void 0;for(c.set(e,t),c.set(t,e);++v0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===l.prototype||(t=function(e){return l.from(e)}(t)),r?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):w(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!n?(t=a.decoder.write(t),a.objectMode||0!==t.length?w(e,a,t,!1):k(e,a)):w(e,a,t,!1))):r||(a.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=E?e=E:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function S(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(d("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?o.nextTick(C,e):C(e))}function C(e){d("emit readable"),e.emit("readable"),T(e)}function k(e,t){t.readingMore||(t.readingMore=!0,o.nextTick(A,e,t))}function A(e,t){for(var n=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):n=function(e,t,n){var r;ei.length?i.length:e;if(a===i.length?o+=i:o+=i.slice(0,e),0===(e-=a)){a===i.length?(++r,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=i.slice(a));break}++r}return t.length-=r,o}(e,t):function(e,t){var n=l.allocUnsafe(e),r=t.head,o=1;r.data.copy(n),e-=r.data.length;for(;r=r.next;){var i=r.data,a=e>i.length?i.length:e;if(i.copy(n,n.length-e,0,a),0===(e-=a)){a===i.length?(++o,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=i.slice(a));break}++o}return t.length-=o,n}(e,t);return r}(e,t.buffer,t.decoder),n);var n}function I(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,o.nextTick(j,t,e))}function j(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function N(e,t){for(var n=0,r=e.length;n=t.highWaterMark||t.ended))return d("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?I(this):S(this),null;if(0===(e=x(e,t))&&t.ended)return 0===t.length&&I(this),null;var r,o=t.needReadable;return d("need readable",o),(0===t.length||t.length-e0?M(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&I(this)),null!==r&&this.emit("data",r),r},b.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},b.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,d("pipe count=%d opts=%j",i.pipesCount,t);var s=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?c:b;function l(t,r){d("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,d("cleanup"),e.removeListener("close",g),e.removeListener("finish",y),e.removeListener("drain",f),e.removeListener("error",m),e.removeListener("unpipe",l),n.removeListener("end",c),n.removeListener("end",b),n.removeListener("data",v),p=!0,!i.awaitDrain||e._writableState&&!e._writableState.needDrain||f())}function c(){d("onend"),e.end()}i.endEmitted?o.nextTick(s):n.once("end",s),e.on("unpipe",l);var f=function(e){return function(){var t=e._readableState;d("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&u(e,"data")&&(t.flowing=!0,T(e))}}(n);e.on("drain",f);var p=!1;var h=!1;function v(t){d("ondata"),h=!1,!1!==e.write(t)||h||((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==N(i.pipes,e))&&!p&&(d("false write response, pause",n._readableState.awaitDrain),n._readableState.awaitDrain++,h=!0),n.pause())}function m(t){d("onerror",t),b(),e.removeListener("error",m),0===u(e,"error")&&e.emit("error",t)}function g(){e.removeListener("finish",y),b()}function y(){d("onfinish"),e.removeListener("close",g),b()}function b(){d("unpipe"),n.unpipe(e)}return n.on("data",v),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",m),e.once("close",g),e.once("finish",y),e.emit("pipe",n),i.flowing||(d("pipe resume"),n.resume()),e},b.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n),this);if(!e){var r=t.pipes,o=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var i=0;i=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(663),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(t,n(31))},function(e,t,n){"use strict";var r=n(141).Buffer,o=r.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function i(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(r.isEncoding===o||!o(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=s,this.end=l,t=4;break;case"utf8":this.fillLast=u,t=4;break;case"base64":this.text=c,this.end=f,t=3;break;default:return this.write=p,void(this.end=d)}this.lastNeed=0,this.lastTotal=0,this.lastChar=r.allocUnsafe(t)}function a(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function u(e){var t=this.lastTotal-this.lastNeed,n=function(e,t,n){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function s(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function l(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function c(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function f(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function p(e){return e.toString(this.encoding)}function d(e){return e&&e.length?this.write(e):""}t.StringDecoder=i,i.prototype.write=function(e){if(0===e.length)return"";var t,n;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n=0)return o>0&&(e.lastNeed=o-1),o;if(--r=0)return o>0&&(e.lastNeed=o-2),o;if(--r=0)return o>0&&(2===o?o=0:e.lastNeed=o-3),o;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)},i.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,n){"use strict";e.exports=i;var r=n(65),o=n(106);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e),this._transformState={afterTransform:function(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(!r)return this.emit("error",new Error("write callback called multiple times"));n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var o=this._readableState;o.reading=!1,(o.needReadable||o.length=0?n&&o?o-1:o:1:!1!==e&&r(e)}},function(e,t,n){"use strict";e.exports=n(679)()?Object.assign:n(680)},function(e,t,n){"use strict";var r,o,i,a,u,s=n(67),l=function(e,t){return t};try{Object.defineProperty(l,"length",{configurable:!0,writable:!1,enumerable:!1,value:1})}catch(e){}1===l.length?(r={configurable:!0,writable:!1,enumerable:!1},o=Object.defineProperty,e.exports=function(e,t){return t=s(t),e.length===t?e:(r.value=t,o(e,"length",r))}):(a=n(317),u=[],i=function(e){var t,n=0;if(u[e])return u[e];for(t=[];e--;)t.push("a"+(++n).toString(36));return new Function("fn","return function ("+t.join(", ")+") { return fn.apply(this, arguments); };")},e.exports=function(e,t){var n;if(t=s(t),e.length===t)return e;n=i(t)(e);try{a(n,e)}catch(e){}return n})},function(e,t,n){"use strict";var r=n(82),o=Object.defineProperty,i=Object.getOwnPropertyDescriptor,a=Object.getOwnPropertyNames,u=Object.getOwnPropertySymbols;e.exports=function(e,t){var n,s=Object(r(t));if(e=Object(r(e)),a(s).forEach(function(r){try{o(e,r,i(t,r))}catch(e){n=e}}),"function"==typeof u&&u(s).forEach(function(r){try{o(e,r,i(t,r))}catch(e){n=e}}),void 0!==n)throw n;return e}},function(e,t,n){"use strict";var r=n(57),o=n(142),i=Function.prototype.call;e.exports=function(e,t){var n={},a=arguments[2];return r(t),o(e,function(e,r,o,u){n[r]=i.call(t,a,e,r,o,u)}),n}},function(e,t){e.exports=function(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e){return{statePlugins:{err:{reducers:(0,i.default)(e),actions:a,selectors:u}}}};var r,o=n(321),i=(r=o)&&r.__esModule?r:{default:r},a=s(n(127)),u=s(n(325));function s(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=s(n(22)),o=s(n(23));t.default=function(e){var t;return t={},(0,r.default)(t,i.NEW_THROWN_ERR,function(t,n){var r=n.payload,i=(0,o.default)(l,r,{type:"thrown"});return t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(i))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_THROWN_ERR_BATCH,function(t,n){var r=n.payload;return r=r.map(function(e){return(0,a.fromJS)((0,o.default)(l,e,{type:"thrown"}))}),t.update("errors",function(e){return(e||(0,a.List)()).concat((0,a.fromJS)(r))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_SPEC_ERR,function(t,n){var r=n.payload,o=(0,a.fromJS)(r);return o=o.set("type","spec"),t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(o)).sortBy(function(e){return e.get("line")})}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_SPEC_ERR_BATCH,function(t,n){var r=n.payload;return r=r.map(function(e){return(0,a.fromJS)((0,o.default)(l,e,{type:"spec"}))}),t.update("errors",function(e){return(e||(0,a.List)()).concat((0,a.fromJS)(r))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.NEW_AUTH_ERR,function(t,n){var r=n.payload,i=(0,a.fromJS)((0,o.default)({},r));return i=i.set("type","auth"),t.update("errors",function(e){return(e||(0,a.List)()).push((0,a.fromJS)(i))}).update("errors",function(t){return(0,u.default)(t,e.getSystem())})}),(0,r.default)(t,i.CLEAR,function(e,t){var n=t.payload;if(!n||!e.get("errors"))return e;var r=e.get("errors").filter(function(e){return e.keySeq().every(function(t){var r=e.get(t),o=n[t];return!o||r!==o})});return e.merge({errors:r})}),(0,r.default)(t,i.CLEAR_BY,function(e,t){var n=t.payload;if(!n||"function"!=typeof n)return e;var r=e.get("errors").filter(function(e){return n(e)});return e.merge({errors:r})}),t};var i=n(127),a=n(7),u=s(n(322));function s(e){return e&&e.__esModule?e:{default:e}}var l={line:0,level:"error",message:"Unknown error"}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){var n={jsSpec:t.specSelectors.specJson().toJS()};return(0,i.default)(u,function(e,t){try{var r=t.transform(e,n);return r.filter(function(e){return!!e})}catch(t){return console.error("Transformer error:",t),e}},e).filter(function(e){return!!e}).map(function(e){return!e.get("line")&&e.get("path"),e})};var r,o=n(727),i=(r=o)&&r.__esModule?r:{default:r};function a(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}var u=[a(n(323)),a(n(324))]},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function(e){return e.map(function(e){var t=e.get("message").indexOf("is not of a type(s)");if(t>-1){var n=e.get("message").slice(t+"is not of a type(s)".length).split(",");return e.set("message",e.get("message").slice(0,t)+function(e){return e.reduce(function(e,t,n,r){return n===r.length-1&&r.length>1?e+"or "+t:r[n+1]&&r.length>2?e+t+", ":r[n+1]?e+t+" ":e+t},"should be a")}(n))}return e})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.transform=function(e,t){t.jsSpec;return e};var r,o=n(138);(r=o)&&r.__esModule,n(7)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.lastError=t.allErrors=void 0;var r=n(7),o=n(58),i=t.allErrors=(0,o.createSelector)(function(e){return e},function(e){return e.get("errors",(0,r.List)())});t.lastError=(0,o.createSelector)(i,function(e){return e.last()})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{layout:{reducers:i.default,actions:a,selectors:u}}}};var r,o=n(327),i=(r=o)&&r.__esModule?r:{default:r},a=s(n(202)),u=s(n(328));function s(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o,i=n(22),a=(r=i)&&r.__esModule?r:{default:r},u=n(7),s=n(202);t.default=(o={},(0,a.default)(o,s.UPDATE_LAYOUT,function(e,t){return e.set("layout",t.payload)}),(0,a.default)(o,s.UPDATE_FILTER,function(e,t){return e.set("filter",t.payload)}),(0,a.default)(o,s.SHOW,function(e,t){var n=t.payload.shown,r=(0,u.fromJS)(t.payload.thing);return e.update("shown",(0,u.fromJS)({}),function(e){return e.set(r,n)})}),(0,a.default)(o,s.UPDATE_MODE,function(e,t){var n=t.payload.thing,r=t.payload.mode;return e.setIn(["modes"].concat(n),(r||"")+"")}),o)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.showSummary=t.whatMode=t.isShown=t.currentFilter=t.current=void 0;var r,o=n(83),i=(r=o)&&r.__esModule?r:{default:r},a=n(58),u=n(9),s=n(7);t.current=function(e){return e.get("layout")},t.currentFilter=function(e){return e.get("filter")};var l=t.isShown=function(e,t,n){return t=(0,u.normalizeArray)(t),e.get("shown",(0,s.fromJS)({})).get((0,s.fromJS)(t),n)};t.whatMode=function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return t=(0,u.normalizeArray)(t),e.getIn(["modes"].concat((0,i.default)(t)),n)},t.showSummary=(0,a.createSelector)(function(e){return e},function(e){return!l(e,"editor")})},function(e,t,n){var r=n(36);e.exports=function(e,t,n,o){try{return o?t(r(n)[0],n[1]):t(n)}catch(t){var i=e.return;throw void 0!==i&&r(i.call(e)),t}}},function(e,t,n){var r=n(70),o=n(19)("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(r.Array===e||i[o]===e)}},function(e,t,n){var r=n(19)("iterator"),o=!1;try{var i=[7][r]();i.return=function(){o=!0},Array.from(i,function(){throw 2})}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var n=!1;try{var i=[7],a=i[r]();a.next=function(){return{done:n=!0}},i[r]=function(){return a},e(i)}catch(e){}return n}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{spec:{wrapActions:s,reducers:i.default,actions:a,selectors:u}}}};var r,o=n(333),i=(r=o)&&r.__esModule?r:{default:r},a=l(n(203)),u=l(n(144)),s=l(n(346));function l(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o=p(n(22)),i=p(n(23)),a=p(n(83)),u=n(7),s=n(9),l=p(n(32)),c=n(144),f=n(203);function p(e){return e&&e.__esModule?e:{default:e}}t.default=(r={},(0,o.default)(r,f.UPDATE_SPEC,function(e,t){return"string"==typeof t.payload?e.set("spec",t.payload):e}),(0,o.default)(r,f.UPDATE_URL,function(e,t){return e.set("url",t.payload+"")}),(0,o.default)(r,f.UPDATE_JSON,function(e,t){return e.set("json",(0,s.fromJSOrdered)(t.payload))}),(0,o.default)(r,f.UPDATE_RESOLVED,function(e,t){return e.setIn(["resolved"],(0,s.fromJSOrdered)(t.payload))}),(0,o.default)(r,f.UPDATE_RESOLVED_SUBTREE,function(e,t){var n=t.payload,r=n.value,o=n.path;return e.setIn(["resolvedSubtrees"].concat((0,a.default)(o)),(0,s.fromJSOrdered)(r))}),(0,o.default)(r,f.UPDATE_PARAM,function(e,t){var n=t.payload,r=n.path,o=n.paramName,i=n.paramIn,u=n.param,l=n.value,c=n.isXml,f=u?(0,s.paramToIdentifier)(u):i+"."+o,p=c?"value_xml":"value";return e.setIn(["meta","paths"].concat((0,a.default)(r),["parameters",f,p]),l)}),(0,o.default)(r,f.UPDATE_EMPTY_PARAM_INCLUSION,function(e,t){var n=t.payload,r=n.pathMethod,o=n.paramName,i=n.paramIn,u=n.includeEmptyValue;if(!o||!i)return console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey."),e;var s=i+"."+o;return e.setIn(["meta","paths"].concat((0,a.default)(r),["parameter_inclusions",s]),u)}),(0,o.default)(r,f.VALIDATE_PARAMS,function(e,t){var n=t.payload,r=n.pathMethod,o=n.isOAS3,i=(0,c.specJsonWithResolvedSubtrees)(e).getIn(["paths"].concat((0,a.default)(r))),l=(0,c.parameterValues)(e,r).toJS();return e.updateIn(["meta","paths"].concat((0,a.default)(r),["parameters"]),(0,u.fromJS)({}),function(t){return i.get("parameters",(0,u.List)()).reduce(function(t,n){var i=(0,s.paramToValue)(n,l),a=(0,c.parameterInclusionSettingFor)(e,r,n.get("name"),n.get("in")),f=(0,s.validateParam)(n,i,{bypassRequiredCheck:a,isOAS3:o});return t.setIn([(0,s.paramToIdentifier)(n),"errors"],(0,u.fromJS)(f))},t)})}),(0,o.default)(r,f.CLEAR_VALIDATE_PARAMS,function(e,t){var n=t.payload.pathMethod;return e.updateIn(["meta","paths"].concat((0,a.default)(n),["parameters"]),(0,u.fromJS)([]),function(e){return e.map(function(e){return e.set("errors",(0,u.fromJS)([]))})})}),(0,o.default)(r,f.SET_RESPONSE,function(e,t){var n=t.payload,r=n.res,o=n.path,a=n.method,u=void 0;(u=r.error?(0,i.default)({error:!0,name:r.err.name,message:r.err.message,statusCode:r.err.statusCode},r.err.response):r).headers=u.headers||{};var c=e.setIn(["responses",o,a],(0,s.fromJSOrdered)(u));return l.default.Blob&&r.data instanceof l.default.Blob&&(c=c.setIn(["responses",o,a,"text"],r.data)),c}),(0,o.default)(r,f.SET_REQUEST,function(e,t){var n=t.payload,r=n.req,o=n.path,i=n.method;return e.setIn(["requests",o,i],(0,s.fromJSOrdered)(r))}),(0,o.default)(r,f.SET_MUTATED_REQUEST,function(e,t){var n=t.payload,r=n.req,o=n.path,i=n.method;return e.setIn(["mutatedRequests",o,i],(0,s.fromJSOrdered)(r))}),(0,o.default)(r,f.UPDATE_OPERATION_META_VALUE,function(e,t){var n=t.payload,r=n.path,o=n.value,i=n.key,s=["paths"].concat((0,a.default)(r)),l=["meta","paths"].concat((0,a.default)(r));return e.getIn(["json"].concat((0,a.default)(s)))||e.getIn(["resolved"].concat((0,a.default)(s)))||e.getIn(["resolvedSubtrees"].concat((0,a.default)(s)))?e.setIn([].concat((0,a.default)(l),[i]),(0,u.fromJS)(o)):e}),(0,o.default)(r,f.CLEAR_RESPONSE,function(e,t){var n=t.payload,r=n.path,o=n.method;return e.deleteIn(["responses",r,o])}),(0,o.default)(r,f.CLEAR_REQUEST,function(e,t){var n=t.payload,r=n.path,o=n.method;return e.deleteIn(["requests",r,o])}),(0,o.default)(r,f.SET_SCHEME,function(e,t){var n=t.payload,r=n.scheme,o=n.path,i=n.method;return o&&i?e.setIn(["scheme",o,i],r):o||i?void 0:e.setIn(["scheme","_defaultScheme"],r)}),r)},function(e,t,n){var r=n(36),o=n(94),i=n(19)("species");e.exports=function(e,t){var n,a=r(e).constructor;return void 0===a||void 0==(n=r(a)[i])?t:o(n)}},function(e,t,n){var r,o,i,a=n(49),u=n(735),s=n(241),l=n(157),c=n(21),f=c.process,p=c.setImmediate,d=c.clearImmediate,h=c.MessageChannel,v=c.Dispatch,m=0,g={},y=function(){var e=+this;if(g.hasOwnProperty(e)){var t=g[e];delete g[e],t()}},b=function(e){y.call(e.data)};p&&d||(p=function(e){for(var t=[],n=1;arguments.length>n;)t.push(arguments[n++]);return g[++m]=function(){u("function"==typeof e?e:Function(e),t)},r(m),m},d=function(e){delete g[e]},"process"==n(93)(f)?r=function(e){f.nextTick(a(y,e,1))}:v&&v.now?r=function(e){v.now(a(y,e,1))}:h?(i=(o=new h).port2,o.port1.onmessage=b,r=a(i.postMessage,i,1)):c.addEventListener&&"function"==typeof postMessage&&!c.importScripts?(r=function(e){c.postMessage(e+"","*")},c.addEventListener("message",b,!1)):r="onreadystatechange"in l("script")?function(e){s.appendChild(l("script")).onreadystatechange=function(){s.removeChild(this),y.call(e)}}:function(e){setTimeout(a(y,e,1),0)}),e.exports={set:p,clear:d}},function(e,t){e.exports=function(e){try{return{e:!1,v:e()}}catch(e){return{e:!0,v:e}}}},function(e,t,n){var r=n(36),o=n(28),i=n(206);e.exports=function(e,t){if(r(e),o(t)&&t.constructor===e)return t;var n=i.f(e);return(0,n.resolve)(t),n.promise}},function(e,t,n){e.exports=n(740)},function(e,t,n){"use strict";t.__esModule=!0;var r,o=n(204),i=(r=o)&&r.__esModule?r:{default:r};t.default=function(e){return function(){var t=e.apply(this,arguments);return new i.default(function(e,n){return function r(o,a){try{var u=t[o](a),s=u.value}catch(e){return void n(e)}if(!u.done)return i.default.resolve(s).then(function(e){r("next",e)},function(e){r("throw",e)});e(s)}("next")})}}},function(e,t,n){"use strict";var r=n(86);e.exports=new r({include:[n(341)]})},function(e,t,n){"use strict";var r=n(86);e.exports=new r({include:[n(209)],implicit:[n(748),n(749),n(750),n(751)]})},function(e,t,n){var r=n(62),o=n(24),i=n(47),a="[object String]";e.exports=function(e){return"string"==typeof e||!o(e)&&i(e)&&r(e)==a}},function(e,t,n){var r=n(147),o=n(79),i=n(135),a=n(38),u=n(80);e.exports=function(e,t,n,s){if(!a(e))return e;for(var l=-1,c=(t=o(t,e)).length,f=c-1,p=e;null!=p&&++l.":"function"==typeof t?" Instead of passing a class like Foo, pass React.createElement(Foo) or .":null!=t&&void 0!==t.props?" This may be caused by unintentionally loading two independent copies of React.":"");var i,u=a.createElement(D,{child:t});if(e){var s=p.get(e);i=s._processChildContext(s._context)}else i=g;var l=N(n);if(l){var c=l._currentElement.props.child;if(_(c,t)){var f=l._renderedComponent.getPublicInstance(),d=o&&function(){o.call(f)};return L._updateRootComponent(l,u,i,n,d),f}L.unmountComponentAtNode(n)}var h=A(n),m=h&&!!O(h),y=I(n),b=m&&!l&&!y,w=L._renderNewRootComponent(u,n,b,i)._renderedComponent.getPublicInstance();return o&&o.call(w),w},render:function(e,t,n){return L._renderSubtreeIntoContainer(null,e,t,n)},unmountComponentAtNode:function(e){j(e)||r("40");var t=N(e);if(!t){I(e),1===e.nodeType&&e.hasAttribute(E);return!1}return delete k[t._instance.rootID],m.batchedUpdates(M,t,e,!1),!0},_mountImageIntoNode:function(e,t,n,i,a){if(j(t)||r("41"),i){var u=A(t);if(d.canReuseMarkup(e,u))return void s.precacheNode(n,u);var l=u.getAttribute(d.CHECKSUM_ATTR_NAME);u.removeAttribute(d.CHECKSUM_ATTR_NAME);var c=u.outerHTML;u.setAttribute(d.CHECKSUM_ATTR_NAME,l);var f=e,p=function(e,t){for(var n=Math.min(e.length,t.length),r=0;r1?r-1:0),a=1;a=o&&(t=console)[e].apply(t,i)}return i.warn=i.bind(null,"warn"),i.error=i.bind(null,"error"),i.info=i.bind(null,"info"),i.debug=i.bind(null,"debug"),{rootInjects:{log:i}}}},function(e,t,n){"use strict";var r,o=n(387),i=(r=o)&&r.__esModule?r:{default:r},a=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(n(393));e.exports=function(e){var t=e.configs,n=e.getConfigs;return{fn:{fetch:i.default.makeHttp(t.preFetch,t.postFetch),buildRequest:i.default.buildRequest,execute:i.default.execute,resolve:i.default.resolve,resolveSubtree:function(e,t,r){for(var o=arguments.length,a=Array(o>3?o-3:0),u=3;u2&&void 0!==arguments[2]?arguments[2]:"",r=(arguments.length>3&&void 0!==arguments[3]?arguments[3]:{}).v2OperationIdCompatibilityMode;return e&&"object"===(void 0===e?"undefined":(0,c.default)(e))?(e.operationId||"").replace(/\s/g,"").length?h(e.operationId):i(t,n,{v2OperationIdCompatibilityMode:r}):null}function i(e,t){if((arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).v2OperationIdCompatibilityMode){var n=(t.toLowerCase()+"_"+e).replace(/[\s!@#$%^&*()_+=[{\]};:<>|.\/?,\\'""-]/g,"_");return(n=n||e.substring(1)+"_"+t).replace(/((_){2,})/g,"_").replace(/^(_)*/g,"").replace(/([_])*$/g,"")}return""+d(t)+h(e)}function a(e,t){return d(t)+"-"+e}function u(e,t){return s(e,t,!0)||null}function s(e,t,n){if(!e||"object"!==(void 0===e?"undefined":(0,c.default)(e))||!e.paths||"object"!==(0,c.default)(e.paths))return null;var r=e.paths;for(var o in r)for(var i in r[o])if("PARAMETERS"!==i.toUpperCase()){var a=r[o][i];if(a&&"object"===(void 0===a?"undefined":(0,c.default)(a))){var u={spec:e,pathName:o,method:i.toUpperCase(),operation:a},s=t(u);if(n&&s)return u}}}Object.defineProperty(t,"__esModule",{value:!0});var l=r(n(18)),c=r(n(1));t.isOAS3=function(e){var t=e.openapi;return!!t&&(0,p.default)(t,"3")},t.isSwagger2=function(e){var t=e.swagger;return!!t&&(0,p.default)(t,"2")},t.opId=o,t.idFromPathMethod=i,t.legacyIdFromPathMethod=a,t.getOperationRaw=function(e,t){return e&&e.paths?u(e,function(e){var n=e.pathName,r=e.method,i=e.operation;if(!i||"object"!==(void 0===i?"undefined":(0,c.default)(i)))return!1;var u=i.operationId;return[o(i,n,r),a(n,r),u].some(function(e){return e&&e===t})}):null},t.findOperation=u,t.eachOperation=s,t.normalizeSwagger=function(e){var t=e.spec,n=t.paths,r={};if(!n||t.$$normalized)return e;for(var i in n){var a=n[i];if((0,f.default)(a)){var u=a.parameters;for(var s in a)!function(e){var n=a[e];if(!(0,f.default)(n))return"continue";var s=o(n,i,e);if(s){r[s]?r[s].push(n):r[s]=[n];var c=r[s];if(c.length>1)c.forEach(function(e,t){e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId=""+s+(t+1)});else if(void 0!==n.operationId){var p=c[0];p.__originalOperationId=p.__originalOperationId||n.operationId,p.operationId=s}}if("parameters"!==e){var d=[],h={};for(var v in t)"produces"!==v&&"consumes"!==v&&"security"!==v||(h[v]=t[v],d.push(h));if(u&&(h.parameters=u,d.push(h)),d.length){var m=!0,g=!1,y=void 0;try{for(var b,_=(0,l.default)(d);!(m=(b=_.next()).done);m=!0){var w=b.value;for(var E in w)if(n[E]){if("parameters"===E){var x=!0,S=!1,C=void 0;try{for(var k,A=(0,l.default)(w[E]);!(x=(k=A.next()).done);x=!0)!function(){var e=k.value;n[E].some(function(t){return t.name&&t.name===e.name||t.$ref&&t.$ref===e.$ref||t.$$ref&&t.$$ref===e.$$ref||t===e})||n[E].push(e)}()}catch(e){S=!0,C=e}finally{try{!x&&A.return&&A.return()}finally{if(S)throw C}}}}else n[E]=w[E]}}catch(e){g=!0,y=e}finally{try{!m&&_.return&&_.return()}finally{if(g)throw y}}}}}(s)}}return t.$$normalized=!0,e};var f=r(n(47)),p=r(n(14)),d=function(e){return String.prototype.toLowerCase.call(e)},h=function(e){return e.replace(/[^\w]/gi,"_")}},function(e,t){e.exports=n(893)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){var n=(arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}).loadSpec,r=void 0!==n&&n,o={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:i(e.headers)},a=o.headers["content-type"],u=r||_(a);return(u?e.text:e.blob||e.buffer).call(e).then(function(e){if(o.text=e,o.data=e,u)try{var t=function(e,t){return t&&(0===t.indexOf("application/json")||t.indexOf("+json")>0)?JSON.parse(e):g.default.safeLoad(e)}(e,a);o.body=t,o.obj=t}catch(e){o.parseError=e}return o})}function i(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t={};return"function"==typeof e.forEach?(e.forEach(function(e,n){void 0!==t[n]?(t[n]=Array.isArray(t[n])?t[n]:[t[n]],t[n].push(e)):t[n]=e}),t):t}function a(e,t){return t||"undefined"==typeof navigator||(t=navigator),t&&"ReactNative"===t.product?!(!e||"object"!==(void 0===e?"undefined":(0,h.default)(e))||"string"!=typeof e.uri):"undefined"!=typeof File?e instanceof File:null!==e&&"object"===(void 0===e?"undefined":(0,h.default)(e))&&"function"==typeof e.pipe}function u(e,t){var n=e.collectionFormat,r=e.allowEmptyValue,o="object"===(void 0===e?"undefined":(0,h.default)(e))?e.value:e;if(void 0===o&&r)return"";if(a(o)||"boolean"==typeof o)return o;var i=encodeURIComponent;return t&&(i=(0,y.default)(o)?function(e){return e}:function(e){return(0,p.default)(e)}),"object"!==(void 0===o?"undefined":(0,h.default)(o))||Array.isArray(o)?Array.isArray(o)?Array.isArray(o)&&!n?o.map(i).join(","):"multi"===n?o.map(i):o.map(i).join({csv:",",ssv:"%20",tsv:"%09",pipes:"|"}[n]):i(o):""}function s(e){var t=(0,f.default)(e).reduce(function(t,n){var r=e[n],o=!!r.skipEncoding,i=o?n:encodeURIComponent(n),a=function(e){return e&&"object"===(void 0===e?"undefined":(0,h.default)(e))}(r)&&!Array.isArray(r);return t[i]=u(a?r:{value:r},o),t},{});return m.default.stringify(t,{encode:!1,indices:!1})||""}function l(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.url,r=void 0===t?"":t,o=e.query,i=e.form;if(i){var l=(0,f.default)(i).some(function(e){return a(i[e].value)}),p=e.headers["content-type"]||e.headers["Content-Type"];if(l||/multipart\/form-data/i.test(p)){var d=n(30);e.body=new d,(0,f.default)(i).forEach(function(t){e.body.append(t,u(i[t],!0))})}else e.body=s(i);delete e.form}if(o){var h=r.split("?"),v=(0,c.default)(h,2),g=v[0],y=v[1],b="";if(y){var _=m.default.parse(y);(0,f.default)(o).forEach(function(e){return delete _[e]}),b=m.default.stringify(_,{encode:!0})}var w=function(){for(var e=arguments.length,t=Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:{};return d.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if("object"===(void 0===t?"undefined":(0,h.default)(t))&&(t=(a=t).url),a.headers=a.headers||{},b.mergeInQueryOrForm(a),!a.requestInterceptor){e.next=10;break}return e.next=6,a.requestInterceptor(a);case 6:if(e.t0=e.sent,e.t0){e.next=9;break}e.t0=a;case 9:a=e.t0;case 10:return n=a.headers["content-type"]||a.headers["Content-Type"],/multipart\/form-data/i.test(n)&&(delete a.headers["content-type"],delete a.headers["Content-Type"]),r=void 0,e.prev=13,e.next=16,(a.userFetch||fetch)(a.url,a);case 16:return r=e.sent,e.next=19,b.serializeRes(r,t,a);case 19:if(r=e.sent,!a.responseInterceptor){e.next=27;break}return e.next=23,a.responseInterceptor(r);case 23:if(e.t1=e.sent,e.t1){e.next=26;break}e.t1=r;case 26:r=e.t1;case 27:e.next=37;break;case 29:if(e.prev=29,e.t2=e.catch(13),r){e.next=33;break}throw e.t2;case 33:throw(o=new Error(r.statusText)).statusCode=o.status=r.status,o.responseError=e.t2,o;case 37:if(r.ok){e.next=42;break}throw(i=new Error(r.statusText)).statusCode=i.status=r.status,i.response=r,i;case 42:return e.abrupt("return",r);case 43:case"end":return e.stop()}},e,this,[[13,29]])}));return function(t){return e.apply(this,arguments)}}();var _=t.shouldDownloadAsText=function(){return/(json|xml|yaml|text)\b/.test(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")}},function(e,t){e.exports=n(41)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){return Array.isArray(e)?e.length<1?"":"/"+e.map(function(e){return(e+"").replace(/~/g,"~0").replace(/\//g,"~1")}).join("/"):e}function i(e,t,n){return{op:"replace",path:e,value:t,meta:n}}function a(e,t,n){return f(c(e.filter(m).map(function(e){return t(e.value,n,e.path)})||[]))}function u(e,t,n){return n=n||[],Array.isArray(e)?e.map(function(e,r){return u(e,t,n.concat(r))}):p(e)?(0,w.default)(e).map(function(r){return u(e[r],t,n.concat(r))}):t(e,n[n.length-1],n)}function s(e,t,n){var r=[];if((n=n||[]).length>0){var o=t(e,n[n.length-1],n);o&&(r=r.concat(o))}if(Array.isArray(e)){var i=e.map(function(e,r){return s(e,t,n.concat(r))});i&&(r=r.concat(i))}else if(p(e)){var a=(0,w.default)(e).map(function(r){return s(e[r],t,n.concat(r))});a&&(r=r.concat(a))}return c(r)}function l(e){return Array.isArray(e)?e:[e]}function c(e){var t;return(t=[]).concat.apply(t,(0,_.default)(e.map(function(e){return Array.isArray(e)?c(e):e})))}function f(e){return e.filter(function(e){return void 0!==e})}function p(e){return e&&"object"===(void 0===e?"undefined":(0,b.default)(e))}function d(e){return e&&"function"==typeof e}function h(e){if(g(e)){var t=e.op;return"add"===t||"remove"===t||"replace"===t}return!1}function v(e){return h(e)||g(e)&&"mutation"===e.type}function m(e){return v(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function g(e){return e&&"object"===(void 0===e?"undefined":(0,b.default)(e))}function y(e,t){try{return S.default.getValueByPointer(e,t)}catch(e){return console.error(e),{}}}Object.defineProperty(t,"__esModule",{value:!0});var b=r(n(1)),_=r(n(34)),w=r(n(0)),E=r(n(35)),x=r(n(2)),S=r(n(36)),C=r(n(4)),k=r(n(37)),A=r(n(38));t.default={add:function(e,t){return{op:"add",path:e,value:t}},replace:i,remove:function(e,t){return{op:"remove",path:e}},merge:function(e,t){return{type:"mutation",op:"merge",path:e,value:t}},mergeDeep:function(e,t){return{type:"mutation",op:"mergeDeep",path:e,value:t}},context:function(e,t){return{type:"context",path:e,value:t}},getIn:function(e,t){return t.reduce(function(e,t){return void 0!==t&&e?e[t]:e},e)},applyPatch:function(e,t,n){if(n=n||{},"merge"===(t=(0,x.default)({},t,{path:t.path&&o(t.path)})).op){var r=y(e,t.path);(0,x.default)(r,t.value),S.default.applyPatch(e,[i(t.path,r)])}else if("mergeDeep"===t.op){var a=y(e,t.path);for(var u in t.value){var s=t.value[u],l=Array.isArray(s);if(l){var c=a[u]||[];a[u]=c.concat(s)}else if(p(s)&&!l){var f=(0,x.default)({},a[u]);for(var d in s){if(Object.prototype.hasOwnProperty.call(f,d)){f=(0,k.default)((0,A.default)({},f),s);break}(0,x.default)(f,(0,E.default)({},d,s[d]))}a[u]=f}else a[u]=s}}else if("add"===t.op&&""===t.path&&p(t.value)){var h=(0,w.default)(t.value).reduce(function(e,n){return e.push({op:"add",path:"/"+o(n),value:t.value[n]}),e},[]);S.default.applyPatch(e,h)}else if("replace"===t.op&&""===t.path){var v=t.value;n.allowMetaPatches&&t.meta&&m(t)&&(Array.isArray(t.value)||p(t.value))&&(v=(0,x.default)({},v,t.meta)),e=v}else if(S.default.applyPatch(e,[t]),n.allowMetaPatches&&t.meta&&m(t)&&(Array.isArray(t.value)||p(t.value))){var g=y(e,t.path),b=(0,x.default)({},g,t.meta);S.default.applyPatch(e,[i(t.path,b)])}return e},parentPathMatch:function(e,t){if(!Array.isArray(t))return!1;for(var n=0,r=t.length;n1&&void 0!==arguments[1]?arguments[1]:{},n=t.requestInterceptor,r=t.responseInterceptor,o=e.withCredentials?"include":"same-origin";return function(t){return e({url:t,loadSpec:!0,requestInterceptor:n,responseInterceptor:r,headers:{Accept:"application/json"},credentials:o}).then(function(e){return e.body})}}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(4)),a=r(n(11));t.makeFetchJSON=o,t.clearCache=function(){s.plugins.refs.clearCache()},t.default=function(e){function t(e){var t=this;E&&(s.plugins.refs.docCache[E]=e),s.plugins.refs.fetchJSON=o(w,{requestInterceptor:y,responseInterceptor:b});var n=[s.plugins.refs];return"function"==typeof g&&n.push(s.plugins.parameters),"function"==typeof m&&n.push(s.plugins.properties),"strict"!==p&&n.push(s.plugins.allOf),(0,l.default)({spec:e,context:{baseDoc:E},plugins:n,allowMetaPatches:h,pathDiscriminator:v,parameterMacro:g,modelPropertyMacro:m}).then(_?function(){var e=(0,a.default)(i.default.mark(function e(n){return i.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",n);case 1:case"end":return e.stop()}},e,t)}));return function(t){return e.apply(this,arguments)}}():c.normalizeSwagger)}var n=e.fetch,r=e.spec,f=e.url,p=e.mode,d=e.allowMetaPatches,h=void 0===d||d,v=e.pathDiscriminator,m=e.modelPropertyMacro,g=e.parameterMacro,y=e.requestInterceptor,b=e.responseInterceptor,_=e.skipNormalization,w=e.http,E=e.baseDoc;return E=E||f,w=n||w||u.default,r?t(r):o(w,{requestInterceptor:y,responseInterceptor:b})(E).then(t)};var u=r(n(7)),s=n(31),l=r(s),c=n(5)},function(e,t){e.exports=n(204)},function(e,t){e.exports=n(91)},function(e,t){e.exports=n(2)},function(e,t){e.exports=n(3)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t){function n(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,n=Array(e),r=0;r-1&&-1===o.indexOf(n)||i.indexOf(u)>-1||a.some(function(e){return u.indexOf(e)>-1})};var r=["properties"],o=["properties"],i=["definitions","parameters","responses","securityDefinitions","components/schemas","components/responses","components/parameters","components/securitySchemes"],a=["schema/example","items/example"]},function(e,t,n){e.exports=n(24)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if("string"==typeof e?n.url=e:n=e,!(this instanceof o))return new o(n);(0,a.default)(this,n);var r=this.resolve().then(function(){return t.disableInterfaces||(0,a.default)(t,o.makeApisTagOperation(t)),t});return r.client=this,r}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(3)),a=r((r(n(25)),n(6))),u=r(n(14)),s=r(n(10)),l=n(7),c=r(l),f=n(16),p=r(f),d=r(n(48)),h=n(49),v=n(51),m=n(5);o.http=c.default,o.makeHttp=l.makeHttp.bind(null,o.http),o.resolve=p.default,o.resolveSubtree=d.default,o.execute=v.execute,o.serializeRes=l.serializeRes,o.serializeHeaders=l.serializeHeaders,o.clearCache=f.clearCache,o.parameterBuilders=v.PARAMETER_BUILDERS,o.makeApisTagOperation=h.makeApisTagOperation,o.buildRequest=v.buildRequest,o.helpers={opId:m.opId},o.prototype={http:c.default,execute:function(e){return this.applyDefaults(),o.execute((0,i.default)({spec:this.spec,http:this.http,securities:{authorized:this.authorizations},contextUrl:"string"==typeof this.url?this.url:void 0},e))},resolve:function(){var e=this;return o.resolve({spec:this.spec,url:this.url,allowMetaPatches:this.allowMetaPatches,requestInterceptor:this.requestInterceptor||null,responseInterceptor:this.responseInterceptor||null}).then(function(t){return e.originalSpec=e.spec,e.spec=t.spec,e.errors=t.errors,e})}},o.prototype.applyDefaults=function(){var e=this.spec,t=this.url;if(t&&(0,u.default)(t,"http")){var n=s.default.parse(t);e.host||(e.host=n.host),e.schemes||(e.schemes=[n.protocol.replace(":","")]),e.basePath||(e.basePath="/")}},t.default=o,e.exports=t.default},function(e,t){e.exports=n(905)},function(e,t){e.exports=n(17)},function(e,t){e.exports=n(906)},function(e,t){e.exports=n(907)},function(e,t){e.exports=n(342)},function(e,t){e.exports=n(910)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0}),t.plugins=t.SpecMap=void 0;var o=r(n(8)),i=r(n(1)),a=r(n(17)),u=r(n(4)),s=r(n(0)),l=r(n(18)),c=r(n(32)),f=r(n(2)),p=r(n(19)),d=r(n(20));t.default=function(e){return new w(e).dispatch()};var h=r(n(33)),v=r(n(9)),m=r(n(39)),g=r(n(43)),y=r(n(44)),b=r(n(45)),_=r(n(46)),w=function(){function e(t){(0,p.default)(this,e),(0,f.default)(this,{spec:"",debugLevel:"info",plugins:[],pluginHistory:{},errors:[],mutations:[],promisedPatches:[],state:{},patches:[],context:{},contextTree:new _.default,showDebug:!1,allPatches:[],pluginProp:"specMap",libMethods:(0,f.default)((0,c.default)(this),v.default),allowMetaPatches:!1},t),this.get=this._get.bind(this),this.getContext=this._getContext.bind(this),this.hasRun=this._hasRun.bind(this),this.wrappedPlugins=this.plugins.map(this.wrapPlugin.bind(this)).filter(v.default.isFunction),this.patches.push(v.default.add([],this.spec)),this.patches.push(v.default.context([],this.context)),this.updatePatches(this.patches)}return(0,d.default)(e,[{key:"debug",value:function(e){if(this.debugLevel===e){for(var t,n=arguments.length,r=Array(n>1?n-1:0),o=1;o1?n-1:0),o=1;o0})}},{key:"nextPromisedPatch",value:function(){if(this.promisedPatches.length>0)return a.default.race(this.promisedPatches.map(function(e){return e.value}))}},{key:"getPluginHistory",value:function(e){var t=this.getPluginName(e);return this.pluginHistory[t]||[]}},{key:"getPluginRunCount",value:function(e){return this.getPluginHistory(e).length}},{key:"getPluginHistoryTip",value:function(e){var t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}},{key:"getPluginMutationIndex",value:function(e){var t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}},{key:"getPluginName",value:function(e){return e.pluginName}},{key:"updatePluginHistory",value:function(e,t){var n=this.getPluginName(e);(this.pluginHistory[n]=this.pluginHistory[n]||[]).push(t)}},{key:"updatePatches",value:function(e,t){var n=this;v.default.normalizeArray(e).forEach(function(e){if(e instanceof Error)n.errors.push(e);else try{if(!v.default.isObject(e))return void n.debug("updatePatches","Got a non-object patch",e);if(n.showDebug&&n.allPatches.push(e),v.default.isPromise(e.value))return n.promisedPatches.push(e),void n.promisedPatchThen(e);if(v.default.isContextPatch(e))return void n.setContext(e.path,e.value);if(v.default.isMutation(e))return void n.updateMutations(e)}catch(e){console.error(e),n.errors.push(e)}})}},{key:"updateMutations",value:function(e){"object"===(0,i.default)(e.value)&&!Array.isArray(e.value)&&this.allowMetaPatches&&(e.value=(0,f.default)({},e.value));var t=v.default.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}},{key:"removePromisedPatch",value:function(e){var t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}},{key:"promisedPatchThen",value:function(e){var t=this;return e.value=e.value.then(function(n){var r=(0,f.default)({},e,{value:n});t.removePromisedPatch(e),t.updatePatches(r)}).catch(function(n){t.removePromisedPatch(e),t.updatePatches(n)})}},{key:"getMutations",value:function(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}},{key:"getCurrentMutations",value:function(){return this.getMutationsForPlugin(this.getCurrentPlugin())}},{key:"getMutationsForPlugin",value:function(e){var t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}},{key:"getCurrentPlugin",value:function(){return this.currentPlugin}},{key:"getPatchesOfType",value:function(e,t){return e.filter(t)}},{key:"getLib",value:function(){return this.libMethods}},{key:"_get",value:function(e){return v.default.getIn(this.state,e)}},{key:"_getContext",value:function(e){return this.contextTree.get(e)}},{key:"setContext",value:function(e,t){return this.contextTree.set(e,t)}},{key:"_hasRun",value:function(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}},{key:"_clone",value:function(e){return JSON.parse((0,o.default)(e))}},{key:"dispatch",value:function(){function e(e){e&&(e=v.default.fullyNormalizeArray(e),n.updatePatches(e,r))}var t=this,n=this,r=this.nextPlugin();if(!r){var o=this.nextPromisedPatch();if(o)return o.then(function(){return t.dispatch()}).catch(function(){return t.dispatch()});var i={spec:this.state,errors:this.errors};return this.showDebug&&(i.patches=this.allPatches),a.default.resolve(i)}if(n.pluginCount=n.pluginCount||{},n.pluginCount[r]=(n.pluginCount[r]||0)+1,n.pluginCount[r]>100)return a.default.resolve({spec:n.state,errors:n.errors.concat(new Error("We've reached a hard limit of 100 plugin runs"))});if(r!==this.currentPlugin&&this.promisedPatches.length){var u=this.promisedPatches.map(function(e){return e.value});return a.default.all(u.map(function(e){return e.then(Function,Function)})).then(function(){return t.dispatch()})}return function(){n.currentPlugin=r;var t=n.getCurrentMutations(),o=n.mutations.length-1;try{if(r.isGenerator){var i=!0,a=!1,u=void 0;try{for(var s,p=(0,l.default)(r(t,n.getLib()));!(i=(s=p.next()).done);i=!0)e(s.value)}catch(e){a=!0,u=e}finally{try{!i&&p.return&&p.return()}finally{if(a)throw u}}}else e(r(t,n.getLib()))}catch(t){console.error(t),e([(0,f.default)((0,c.default)(t),{plugin:r})])}finally{n.updatePluginHistory(r,{mutationIndex:o})}return n.dispatch()}()}}]),e}(),E={refs:m.default,allOf:g.default,parameters:y.default,properties:b.default};t.SpecMap=w,t.plugins=E},function(e,t){e.exports=n(349)},function(e,t){e.exports=n(288)},function(e,t){e.exports=n(83)},function(e,t){e.exports=n(22)},function(e,t){e.exports=n(911)},function(e,t){e.exports=n(179)},function(e,t){e.exports=n(181)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){if(!A.test(e)){if(!t)throw new O("Tried to resolve a relative URL, without having a basePath. path: '"+e+"' basePath: '"+t+"'");return x.default.resolve(t,e)}return e}function i(e,t){var n;return n=e&&e.response&&e.response.body?e.response.body.code+" "+e.response.body.message:e.message,new O("Could not resolve reference: "+n,t,e)}function a(e){return(e+"").split("#")}function u(e,t){var n=P[e];if(n&&!S.default.isPromise(n))try{var r=l(t,n);return(0,b.default)(g.default.resolve(r),{__value:r})}catch(e){return g.default.reject(e)}return s(e).then(function(e){return l(t,e)})}function s(e){var t=P[e];return t?S.default.isPromise(t)?t:g.default.resolve(t):(P[e]=I.fetchJSON(e).then(function(t){return P[e]=t,t}),P[e])}function l(e,t){var n=c(e);if(n.length<1)return t;var r=S.default.getIn(t,n);if(void 0===r)throw new O("Could not resolve pointer: "+e+" does not exist in document",{pointer:e});return r}function c(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a "+(void 0===e?"undefined":(0,v.default)(e)));return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(f)}function f(e){return"string"!=typeof e?e:E.default.unescape(e.replace(/~1/g,"/").replace(/~0/g,"~"))}function p(e){return E.default.escape(e.replace(/~/g,"~0").replace(/\//g,"~1"))}function d(e,t){if(j(t))return!0;var n=e.charAt(t.length),r=t.slice(-1);return 0===e.indexOf(t)&&(!n||"/"===n||"#"===n)&&"#"!==r}function h(e,t,n,r){var o=T.get(r);o||(o={},T.set(r,o));var i=function(e){return 0===e.length?"":"/"+e.map(p).join("/")}(n),a=(t||"")+"#"+e;if(t==r.contextTree.get([]).baseDoc&&d(i,e))return!0;var u="";if(n.some(function(e){return u=u+"/"+p(e),o[u]&&o[u].some(function(e){return d(e,a)||d(a,e)})}))return!0;o[i]=(o[i]||[]).concat(a)}Object.defineProperty(t,"__esModule",{value:!0});var v=r(n(1)),m=r(n(0)),g=r(n(17)),y=r(n(40)),b=r(n(2)),_=n(41),w=r(n(15)),E=r(n(42)),x=r(n(10)),S=r(n(9)),C=r(n(21)),k=n(22),A=new RegExp("^([a-z]+://|//)","i"),O=(0,C.default)("JSONRefError",function(e,t,n){this.originalError=n,(0,b.default)(this,t||{})}),P={},T=new y.default,M={key:"$ref",plugin:function(e,t,n,r){var s=n.slice(0,-1);if(!(0,k.isFreelyNamed)(s)){var l=r.getContext(n).baseDoc;if("string"!=typeof e)return new O("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:l,fullPath:n});var f=a(e),p=f[0],d=f[1]||"",v=void 0;try{v=l||p?o(p,l):null}catch(t){return i(t,{pointer:d,$ref:e,basePath:v,fullPath:n})}var g=void 0,y=void 0;if(!h(d,v,s,r)){if(null==v?(y=c(d),void 0===(g=r.get(y))&&(g=new O("Could not resolve reference: "+e,{pointer:d,$ref:e,baseDoc:l,fullPath:n}))):g=null!=(g=u(v,d)).__value?g.__value:g.catch(function(t){throw i(t,{pointer:d,$ref:e,baseDoc:l,fullPath:n})}),g instanceof Error)return[S.default.remove(n),g];var b=S.default.replace(s,g,{$$ref:e});if(v&&v!==l)return[b,S.default.context(s,{baseDoc:v})];try{if(!function(e,t){var n=[e];return t.path.reduce(function(e,t){return n.push(e[t]),e[t]},e),function e(t){return S.default.isObject(t)&&(n.indexOf(t)>=0||(0,m.default)(t).some(function(n){return e(t[n])}))}(t.value)}(r.state,b))return b}catch(e){return null}}}}},I=(0,b.default)(M,{docCache:P,absoluteify:o,clearCache:function(e){void 0!==e?delete P[e]:(0,m.default)(P).forEach(function(e){delete P[e]})},JSONRefError:O,wrapError:i,getDoc:s,split:a,extractFromDoc:u,fetchJSON:function(e){return(0,_.fetch)(e,{headers:{Accept:"application/json, application/yaml"},loadSpec:!0}).then(function(e){return e.text()}).then(function(e){return w.default.safeLoad(e)})},extract:l,jsonPointerToArray:c,unescapeJsonPointerToken:f});t.default=I;var j=function(e){return!e||"/"===e||"#"===e};e.exports=t.default},function(e,t){e.exports=n(914)},function(e,t){e.exports=n(925)},function(e,t){e.exports=n(926)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){return e&&e.__esModule?e:{default:e}}(n(2)),o=n(22);t.default={key:"allOf",plugin:function(e,t,n,i,a){if(!a.meta||!a.meta.$$ref){var u=n.slice(0,-1);if(!(0,o.isFreelyNamed)(u)){if(!Array.isArray(e)){var s=new TypeError("allOf must be an array");return s.fullPath=n,s}var l=!1,c=a.value;u.forEach(function(e){c&&(c=c[e])}),delete(c=(0,r.default)({},c)).allOf;var f=[i.replace(u,{})].concat(e.map(function(e,t){if(!i.isObject(e)){if(l)return null;l=!0;var r=new TypeError("Elements in allOf must be objects");return r.fullPath=n,r}return i.mergeDeep(u,e)}));return f.push(i.mergeDeep(u,c)),c.$$ref||f.push(i.remove([].concat(u,"$$ref"))),f}}}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(2)),i=r(n(9));t.default={key:"parameters",plugin:function(e,t,n,r,a){if(Array.isArray(e)&&e.length){var u=(0,o.default)([],e),s=n.slice(0,-1),l=(0,o.default)({},i.default.getIn(r.spec,s));return e.forEach(function(e,t){try{u[t].default=r.parameterMacro(l,e)}catch(e){var o=new Error(e);return o.fullPath=n,o}}),i.default.replace(n,u)}return i.default.replace(n,e)}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(2)),i=r(n(9));t.default={key:"properties",plugin:function(e,t,n,r){var a=(0,o.default)({},e);for(var u in e)try{a[u].default=r.modelPropertyMacro(a[u])}catch(e){var s=new Error(e);return s.fullPath=n,s}return i.default.replace(n,a)}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e,t){return i({children:{}},e,t)}function i(e,t,n){return e.value=t||{},e.protoValue=n?(0,u.default)({},n.protoValue,e.value):e.value,(0,a.default)(e.children).forEach(function(t){var n=e.children[t];e.children[t]=i(n,n.value,e)}),e}Object.defineProperty(t,"__esModule",{value:!0});var a=r(n(0)),u=r(n(3)),s=r(n(19)),l=r(n(20)),c=function(){function e(t){(0,s.default)(this,e),this.root=o(t||{})}return(0,l.default)(e,[{key:"set",value:function(e,t){var n=this.getParent(e,!0);if(n){var r=e[e.length-1],a=n.children;a[r]?i(a[r],t,n):a[r]=o(t,n)}else i(this.root,t,null)}},{key:"get",value:function(e){if((e=e||[]).length<1)return this.root.value;for(var t=this.root,n=void 0,r=void 0,o=0;o2&&void 0!==arguments[2]?arguments[2]:{};return o.default.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return r=y.returnEntireTree,a=y.baseDoc,c=y.requestInterceptor,f=y.responseInterceptor,p=y.parameterMacro,d=y.modelPropertyMacro,h={pathDiscriminator:n,baseDoc:a,requestInterceptor:c,responseInterceptor:f,parameterMacro:p,modelPropertyMacro:d},v=(0,l.normalizeSwagger)({spec:t}),m=v.spec,e.next=5,(0,s.default)((0,i.default)({},h,{spec:m,allowMetaPatches:!0,skipNormalization:!0}));case 5:return g=e.sent,!r&&Array.isArray(n)&&n.length&&(g.spec=(0,u.default)(g.spec,n)||null),e.abrupt("return",g);case 8:case"end":return e.stop()}},e,this)}));return function(t,n){return e.apply(this,arguments)}}(),e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return function(t){var n=t.pathName,r=t.method,o=t.operationId;return function(t){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.execute((0,a.default)({spec:e.spec},(0,u.default)(e,"requestInterceptor","responseInterceptor","userFetch"),{pathName:n,method:r,parameters:t,operationId:o},i))}}}function i(e){var t=e.spec,n=e.cb,r=void 0===n?l:n,o=e.defaultTag,i=void 0===o?"default":o,a=e.v2OperationIdCompatibilityMode,u={},f={};return(0,s.eachOperation)(t,function(e){var n=e.pathName,o=e.method,l=e.operation;(l.tags?c(l.tags):[i]).forEach(function(e){if("string"==typeof e){var i=f[e]=f[e]||{},c=(0,s.opId)(l,n,o,{v2OperationIdCompatibilityMode:a}),p=r({spec:t,pathName:n,method:o,operation:l,operationId:c});if(u[c])u[c]++,i[""+c+u[c]]=p;else if(void 0!==i[c]){var d=u[c]||1;u[c]=d+1,i[""+c+u[c]]=p;var h=i[c];delete i[c],i[""+c+d]=h}else i[c]=p}})}),f}Object.defineProperty(t,"__esModule",{value:!0}),t.self=void 0;var a=r(n(3));t.makeExecute=o,t.makeApisTagOperationsOperationExecute=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=f.makeExecute(e),n=f.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t}),r={};for(var o in n)for(var i in r[o]={operations:{}},n[o])r[o].operations[i]={execute:n[o][i]};return{apis:r}},t.makeApisTagOperation=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=f.makeExecute(e);return{apis:f.mapTagOperations({v2OperationIdCompatibilityMode:e.v2OperationIdCompatibilityMode,spec:e.spec,cb:t})}},t.mapTagOperations=i;var u=r(n(50)),s=n(5),l=function(){return null},c=function(e){return Array.isArray(e)?e:[e]},f=t.self={mapTagOperations:i,makeExecute:o}},function(e,t){e.exports=n(927)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.spec,n=e.operationId,r=(e.securities,e.requestContentType,e.responseContentType),o=e.scheme,a=e.requestInterceptor,s=e.responseInterceptor,c=e.contextUrl,f=e.userFetch,p=(e.requestBody,e.server),d=e.serverVariables,h=e.http,g=e.parameters,y=e.parameterBuilders,O=(0,x.isOAS3)(t);y||(y=O?_.default:b.default);var P={url:"",credentials:h&&h.withCredentials?"include":"same-origin",headers:{},cookies:{}};a&&(P.requestInterceptor=a),s&&(P.responseInterceptor=s),f&&(P.userFetch=f);var T=(0,x.getOperationRaw)(t,n);if(!T)throw new C("Operation "+n+" not found");var M=T.operation,I=void 0===M?{}:M,j=T.method,N=T.pathName;if(P.url+=i({spec:t,scheme:o,contextUrl:c,server:p,serverVariables:d,pathName:N,method:j}),!n)return delete P.cookies,P;P.url+=N,P.method=(""+j).toUpperCase(),g=g||{};var R=t.paths[N]||{};r&&(P.headers.accept=r);var D=A([].concat(S(I.parameters)).concat(S(R.parameters)));D.forEach(function(e){var n=y[e.in],r=void 0;if("body"===e.in&&e.schema&&e.schema.properties&&(r=g),void 0===(r=e&&e.name&&g[e.name])?r=e&&e.name&&g[e.in+"."+e.name]:k(e.name,D).length>1&&console.warn("Parameter '"+e.name+"' is ambiguous because the defined spec has more than one parameter with the name: '"+e.name+"' and the passed-in parameter values did not define an 'in' value."),null!==r){if(void 0!==e.default&&void 0===r&&(r=e.default),void 0===r&&e.required&&!e.allowEmptyValue)throw new Error("Required parameter "+e.name+" is not provided");if(O&&e.schema&&"object"===e.schema.type&&"string"==typeof r)try{r=JSON.parse(r)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}n&&n({req:P,parameter:e,value:r,operation:I,spec:t})}});var L=(0,u.default)({},e,{operation:I});if((P=O?(0,w.default)(L,P):(0,E.default)(L,P)).cookies&&(0,l.default)(P.cookies).length){var U=(0,l.default)(P.cookies).reduce(function(e,t){var n=P.cookies[t];return e+(e?"&":"")+v.default.serialize(t,n)},"");P.headers.Cookie=U}return P.cookies&&delete P.cookies,(0,m.mergeInQueryOrForm)(P),P}function i(e){return(0,x.isOAS3)(e.spec)?function(e){var t=e.spec,n=e.pathName,r=e.method,o=e.server,i=e.contextUrl,a=e.serverVariables,u=void 0===a?{}:a,s=(0,f.default)(t,["paths",n,(r||"").toLowerCase(),"servers"])||(0,f.default)(t,["paths",n,"servers"])||(0,f.default)(t,["servers"]),l="",c=null;if(o&&s&&s.length){var p=s.map(function(e){return e.url});p.indexOf(o)>-1&&(l=o,c=s[p.indexOf(o)])}!l&&s&&s.length&&(l=s[0].url,c=s[0]),l.indexOf("{")>-1&&function(e){for(var t=[],n=/{([^}]+)}/g,r=void 0;r=n.exec(e);)t.push(r[1]);return t}(l).forEach(function(e){if(c.variables&&c.variables[e]){var t=c.variables[e],n=u[e]||t.default,r=new RegExp("{"+e+"}","g");l=l.replace(r,n)}});return function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",n=h.default.parse(e),r=h.default.parse(t),o=P(n.protocol)||P(r.protocol)||"",i=n.host||r.host,a=n.pathname||"",u=void 0;return"/"===(u=o&&i?o+"://"+(i+a):a)[u.length-1]?u.slice(0,-1):u}(l,i)}(e):function(e){var t=e.spec,n=e.scheme,r=e.contextUrl,o=void 0===r?"":r,i=h.default.parse(o),a=Array.isArray(t.schemes)?t.schemes[0]:null,u=n||a||P(i.protocol)||"http",s=t.host||i.host||"",l=t.basePath||"",c=void 0;return"/"===(c=u&&s?u+"://"+(s+l):l)[c.length-1]?c.slice(0,-1):c}(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.self=void 0;var a=r(n(8)),u=r(n(3)),s=r(n(52)),l=r(n(0)),c=r(n(2));t.execute=function(e){var t=e.http,n=e.fetch,r=e.spec,o=e.operationId,i=e.pathName,l=e.method,c=e.parameters,f=e.securities,h=(0,s.default)(e,["http","fetch","spec","operationId","pathName","method","parameters","securities"]),v=t||n||g.default;i&&l&&!o&&(o=(0,x.legacyIdFromPathMethod)(i,l));var m=O.buildRequest((0,u.default)({spec:r,operationId:o,parameters:c,securities:f,http:v},h));return m.body&&((0,p.default)(m.body)||(0,d.default)(m.body))&&(m.body=(0,a.default)(m.body)),v(m)},t.buildRequest=o,t.baseUrl=i;var f=r((r(n(6)),n(12))),p=r(n(53)),d=r(n(54)),h=r((r(n(13)),n(10))),v=r(n(55)),m=n(7),g=r(m),y=r(n(21)),b=r(n(56)),_=r(n(57)),w=r(n(62)),E=r(n(64)),x=n(5),S=function(e){return Array.isArray(e)?e:[]},C=(0,y.default)("OperationNotFoundError",function(e,t,n){this.originalError=n,(0,c.default)(this,t||{})}),k=function(e,t){return t.filter(function(t){return t.name===e})},A=function(e){var t={};e.forEach(function(e){t[e.in]||(t[e.in]={}),t[e.in][e.name]=e});var n=[];return(0,l.default)(t).forEach(function(e){(0,l.default)(t[e]).forEach(function(r){n.push(t[e][r])})}),n},O=t.self={buildRequest:o},P=function(e){return e?e.replace(/\W/g,""):null}},function(e,t){e.exports=n(84)},function(e,t){e.exports=n(228)},function(e,t){e.exports=n(24)},function(e,t){e.exports=n(930)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={body:function(e){var t=e.req,n=e.value;t.body=n},header:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{},void 0!==r&&(t.headers[n.name]=r)},query:function(e){var t=e.req,n=e.value,r=e.parameter;if(t.query=t.query||{},!1===n&&"boolean"===r.type&&(n="false"),0===n&&["number","integer"].indexOf(r.type)>-1&&(n="0"),n)t.query[r.name]={collectionFormat:r.collectionFormat,value:n};else if(r.allowEmptyValue&&void 0!==n){var o=r.name;t.query[o]=t.query[o]||{},t.query[o].allowEmptyValue=!0}},path:function(e){var t=e.req,n=e.value,r=e.parameter;t.url=t.url.split("{"+r.name+"}").join(encodeURIComponent(n))},formData:function(e){var t=e.req,n=e.value,r=e.parameter;(n||r.allowEmptyValue)&&(t.form=t.form||{},t.form[r.name]={value:n,allowEmptyValue:r.allowEmptyValue,collectionFormat:r.collectionFormat})}},e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(0)),i=r(n(1)),a=r(n(58));t.default={path:function(e){var t=e.req,n=e.value,r=e.parameter,o=r.name,i=r.style,u=r.explode,s=(0,a.default)({key:r.name,value:n,style:i||"simple",explode:u||!1,escape:!0});t.url=t.url.split("{"+o+"}").join(s)},query:function(e){var t=e.req,n=e.value,r=e.parameter;if(t.query=t.query||{},!1===n&&(n="false"),0===n&&(n="0"),n){var u=void 0===n?"undefined":(0,i.default)(n);"deepObject"===r.style?(0,o.default)(n).forEach(function(e){var o=n[e];t.query[r.name+"["+e+"]"]={value:(0,a.default)({key:e,value:o,style:"deepObject",escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}}):"object"!==u||Array.isArray(n)||"form"!==r.style&&r.style||!r.explode&&void 0!==r.explode?t.query[r.name]={value:(0,a.default)({key:r.name,value:n,style:r.style||"form",explode:void 0===r.explode||r.explode,escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}:(0,o.default)(n).forEach(function(e){var o=n[e];t.query[e]={value:(0,a.default)({key:e,value:o,style:r.style||"form",escape:r.allowReserved?"unsafe":"reserved"}),skipEncoding:!0}})}else if(r.allowEmptyValue&&void 0!==n){var s=r.name;t.query[s]=t.query[s]||{},t.query[s].allowEmptyValue=!0}},header:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{},u.indexOf(n.name.toLowerCase())>-1||void 0!==r&&(t.headers[n.name]=(0,a.default)({key:n.name,value:r,style:n.style||"simple",explode:void 0!==n.explode&&n.explode,escape:!1}))},cookie:function(e){var t=e.req,n=e.parameter,r=e.value;t.headers=t.headers||{};var o=void 0===r?"undefined":(0,i.default)(r);if("undefined"!==o){var u="object"===o&&!Array.isArray(r)&&n.explode?"":n.name+"=";t.headers.Cookie=u+(0,a.default)({key:n.name,value:r,escape:!1,style:n.style||"form",explode:void 0!==n.explode&&n.explode})}}};var u=["accept","authorization","content-type"];e.exports=t.default},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).escape,n=arguments[2];return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&t?n?JSON.parse(e):(0,s.stringToCharArray)(e).map(function(e){return c(e)?e:l(e)&&"unsafe"===t?e:((0,u.default)(e)||[]).map(function(e){return("0"+e.toString(16).toUpperCase()).slice(-2)}).map(function(e){return"%"+e}).join("")}).join(""):e}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(0)),a=r(n(1));t.encodeDisallowedCharacters=o,t.default=function(e){var t=e.value;return Array.isArray(t)?function(e){var t=e.key,n=e.value,r=e.style,i=e.explode,a=e.escape,u=function(e){return o(e,{escape:a})};if("simple"===r)return n.map(function(e){return u(e)}).join(",");if("label"===r)return"."+n.map(function(e){return u(e)}).join(".");if("matrix"===r)return n.map(function(e){return u(e)}).reduce(function(e,n){return!e||i?(e||"")+";"+t+"="+n:e+","+n},"");if("form"===r){var s=i?"&"+t+"=":",";return n.map(function(e){return u(e)}).join(s)}if("spaceDelimited"===r){var l=i?t+"=":"";return n.map(function(e){return u(e)}).join(" "+l)}if("pipeDelimited"===r){var c=i?t+"=":"";return n.map(function(e){return u(e)}).join("|"+c)}}(e):"object"===(void 0===t?"undefined":(0,a.default)(t))?function(e){var t=e.key,n=e.value,r=e.style,a=e.explode,u=e.escape,s=function(e){return o(e,{escape:u})},l=(0,i.default)(n);return"simple"===r?l.reduce(function(e,t){var r=s(n[t]);return(e?e+",":"")+t+(a?"=":",")+r},""):"label"===r?l.reduce(function(e,t){var r=s(n[t]);return(e?e+".":".")+t+(a?"=":".")+r},""):"matrix"===r&&a?l.reduce(function(e,t){var r=s(n[t]);return(e?e+";":";")+t+"="+r},""):"matrix"===r?l.reduce(function(e,r){var o=s(n[r]);return(e?e+",":";"+t+"=")+r+","+o},""):"form"===r?l.reduce(function(e,t){var r=s(n[t]);return(e?e+(a?"&":","):"")+t+(a?"=":",")+r},""):void 0}(e):function(e){var t=e.key,n=e.value,r=e.style,i=e.escape,a=function(e){return o(e,{escape:i})};return"simple"===r?a(n):"label"===r?"."+a(n):"matrix"===r?";"+t+"="+a(n):"form"===r?a(n):"deepObject"===r?a(n):void 0}(e)};var u=r((r(n(59)),n(60))),s=n(61),l=function(e){return":/?#[]@!$&'()*+,;=".indexOf(e)>-1},c=function(e){return/^[a-z0-9\-._~]+$/i.test(e)}},function(e,t){e.exports=n(931)},function(e,t){e.exports=n(932)},function(e,t){e.exports=n(933)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,i=void 0===o?{}:o,a=e.spec,f=(0,s.default)({},t),p=r.authorized,d=void 0===p?{}:p,h=i.security||a.security||[],v=d&&!!(0,u.default)(d).length,m=(0,l.default)(a,["components","securitySchemes"])||{};return f.headers=f.headers||{},f.query=f.query||{},(0,u.default)(r).length&&v&&h&&(!Array.isArray(i.security)||i.security.length)?(h.forEach(function(e,t){for(var n in e){var r=d[n],o=m[n];if(r){var i=r.value||r,a=o.type;if(r)if("apiKey"===a)"query"===o.in&&(f.query[o.name]=i),"header"===o.in&&(f.headers[o.name]=i),"cookie"===o.in&&(f.cookies[o.name]=i);else if("http"===a){if("basic"===o.scheme){var u=i.username,s=i.password,l=(0,c.default)(u+":"+s);f.headers.Authorization="Basic "+l}"bearer"===o.scheme&&(f.headers.Authorization="Bearer "+i)}else if("oauth2"===a){var p=r.token||{},h=p.access_token,v=p.token_type;v&&"bearer"!==v.toLowerCase()||(v="Bearer"),f.headers.Authorization=v+" "+h}}}}),f):t}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(8)),a=r(n(1)),u=r(n(0));t.default=function(e,t){var n=e.operation,r=e.requestBody,s=e.securities,l=e.spec,c=e.attachContentTypeForEmptyPayload,p=e.requestContentType;t=o({request:t,securities:s,operation:n,spec:l});var d=n.requestBody||{},h=(0,u.default)(d.content||{}),v=p&&h.indexOf(p)>-1;if(r||c){if(p&&v)t.headers["Content-Type"]=p;else if(!p){var m=h[0];m&&(t.headers["Content-Type"]=m,p=m)}}else p&&v&&(t.headers["Content-Type"]=p);return r&&(p?h.indexOf(p)>-1&&("application/x-www-form-urlencoded"===p||0===p.indexOf("multipart/")?"object"===(void 0===r?"undefined":(0,a.default)(r))?(t.form={},(0,u.default)(r).forEach(function(e){var n,o=r[e],u=void 0;"undefined"!=typeof File&&(u=o instanceof File),"undefined"!=typeof Blob&&(u=u||o instanceof Blob),void 0!==f.Buffer&&(u=u||f.Buffer.isBuffer(o)),n="object"!==(void 0===o?"undefined":(0,a.default)(o))||u?o:Array.isArray(o)?o.toString():(0,i.default)(o),t.form[e]={value:n}})):t.form=r:t.body=r):t.body=r),t},t.applySecurities=o;var s=r(n(6)),l=r(n(12)),c=r(n(13)),f=n(63)},function(e,t){e.exports=n(54)},function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function o(e){var t=e.request,n=e.securities,r=void 0===n?{}:n,o=e.operation,s=void 0===o?{}:o,l=e.spec,c=(0,u.default)({},t),f=r.authorized,p=void 0===f?{}:f,d=r.specSecurity,h=void 0===d?[]:d,v=s.security||h,m=p&&!!(0,i.default)(p).length,g=l.securityDefinitions;return c.headers=c.headers||{},c.query=c.query||{},(0,i.default)(r).length&&m&&v&&(!Array.isArray(s.security)||s.security.length)?(v.forEach(function(e,t){for(var n in e){var r=p[n];if(r){var o=r.token,i=r.value||r,u=g[n],s=u.type,l=u["x-tokenName"]||"access_token",f=o&&o[l],d=o&&o.token_type;if(r)if("apiKey"===s){var h="query"===u.in?"query":"headers";c[h]=c[h]||{},c[h][u.name]=i}else"basic"===s?i.header?c.headers.authorization=i.header:(i.base64=(0,a.default)(i.username+":"+i.password),c.headers.authorization="Basic "+i.base64):"oauth2"===s&&f&&(d=d&&"bearer"!==d.toLowerCase()?d:"Bearer",c.headers.authorization=d+" "+f)}}}),c):t}Object.defineProperty(t,"__esModule",{value:!0});var i=r(n(0));t.default=function(e,t){var n=e.spec,r=e.operation,i=e.securities,a=e.requestContentType,u=e.attachContentTypeForEmptyPayload;if((t=o({request:t,securities:i,operation:r,spec:n})).body||t.form||u)a?t.headers["Content-Type"]=a:Array.isArray(r.consumes)?t.headers["Content-Type"]=r.consumes[0]:Array.isArray(n.consumes)?t.headers["Content-Type"]=n.consumes[0]:r.parameters&&r.parameters.filter(function(e){return"file"===e.type}).length?t.headers["Content-Type"]="multipart/form-data":r.parameters&&r.parameters.filter(function(e){return"formData"===e.in}).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(a){var s=r.parameters&&r.parameters.filter(function(e){return"body"===e.in}).length>0,l=r.parameters&&r.parameters.filter(function(e){return"formData"===e.in}).length>0;(s||l)&&(t.headers["Content-Type"]=a)}return t},t.applySecurities=o;var a=r(n(13)),u=r(n(6));r(n(7))}])},function(e,t,n){"use strict";var r=Object.prototype.hasOwnProperty,o=function(){for(var e=[],t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e}();t.arrayToObject=function(e,t){for(var n=t&&t.plainObjects?Object.create(null):{},r=0;r=48&&i<=57||i>=65&&i<=90||i>=97&&i<=122?n+=t.charAt(r):i<128?n+=o[i]:i<2048?n+=o[192|i>>6]+o[128|63&i]:i<55296||i>=57344?n+=o[224|i>>12]+o[128|i>>6&63]+o[128|63&i]:(r+=1,i=65536+((1023&i)<<10|1023&t.charCodeAt(r)),n+=o[240|i>>18]+o[128|i>>12&63]+o[128|i>>6&63]+o[128|63&i])}return n},t.compact=function(e){for(var t=[{obj:{o:e},prop:"o"}],n=[],r=0;r=0;l--)if(f[l]!=p[l])return!1;for(l=f.length-1;l>=0;l--)if(c=f[l],!a(e[c],t[c],n))return!1;return typeof e==typeof t}(e,t,n))};function u(e){return null===e||void 0===e}function s(e){return!(!e||"object"!=typeof e||"number"!=typeof e.length)&&("function"==typeof e.copy&&"function"==typeof e.slice&&!(e.length>0&&"number"!=typeof e[0]))}},function(e,t,n){var r={strict:!0},o=n(390),i=function(e,t){return o(e,t,r)},a=n(231);t.JsonPatchError=a.PatchError,t.deepClone=a._deepClone;var u={add:function(e,t,n){return e[t]=this.value,{newDocument:n}},remove:function(e,t,n){var r=e[t];return delete e[t],{newDocument:n,removed:r}},replace:function(e,t,n){var r=e[t];return e[t]=this.value,{newDocument:n,removed:r}},move:function(e,t,n){var r=l(n,this.path);r&&(r=a._deepClone(r));var o=c(n,{op:"remove",path:this.from}).removed;return c(n,{op:"add",path:this.path,value:o}),{newDocument:n,removed:r}},copy:function(e,t,n){var r=l(n,this.from);return c(n,{op:"add",path:this.path,value:a._deepClone(r)}),{newDocument:n}},test:function(e,t,n){return{newDocument:n,test:i(e[t],this.value)}},_get:function(e,t,n){return this.value=e[t],{newDocument:n}}},s={add:function(e,t,n){return a.isInteger(t)?e.splice(t,0,this.value):e[t]=this.value,{newDocument:n,index:t}},remove:function(e,t,n){return{newDocument:n,removed:e.splice(t,1)[0]}},replace:function(e,t,n){var r=e[t];return e[t]=this.value,{newDocument:n,removed:r}},move:u.move,copy:u.copy,test:u.test,_get:u._get};function l(e,t){if(""==t)return e;var n={op:"_get",path:t};return c(e,n),n.value}function c(e,n,r,o){if(void 0===r&&(r=!1),void 0===o&&(o=!0),r&&("function"==typeof r?r(n,0,e,n.path):p(n,0)),""===n.path){var c={newDocument:e};if("add"===n.op)return c.newDocument=n.value,c;if("replace"===n.op)return c.newDocument=n.value,c.removed=e,c;if("move"===n.op||"copy"===n.op)return c.newDocument=l(e,n.from),"move"===n.op&&(c.removed=e),c;if("test"===n.op){if(c.test=i(e,n.value),!1===c.test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,n,e);return c.newDocument=e,c}if("remove"===n.op)return c.removed=e,c.newDocument=null,c;if("_get"===n.op)return n.value=e,c;if(r)throw new t.JsonPatchError("Operation `op` property is not one of operations defined in RFC-6902","OPERATION_OP_INVALID",0,n,e);return c}o||(e=a._deepClone(e));var f=(n.path||"").split("/"),d=e,h=1,v=f.length,m=void 0,g=void 0,y=void 0;for(y="function"==typeof r?r:p;;){if(g=f[h],r&&void 0===m&&(void 0===d[g]?m=f.slice(0,h).join("/"):h==v-1&&(m=n.path),void 0!==m&&y(n,0,e,m)),h++,Array.isArray(d)){if("-"===g)g=d.length;else{if(r&&!a.isInteger(g))throw new t.JsonPatchError("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",0,n.path,n);a.isInteger(g)&&(g=~~g)}if(h>=v){if(r&&"add"===n.op&&g>d.length)throw new t.JsonPatchError("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",0,n.path,n);if(!1===(c=s[n.op].call(n,d,g,e)).test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,n,e);return c}}else if(g&&-1!=g.indexOf("~")&&(g=a.unescapePathComponent(g)),h>=v){if(!1===(c=u[n.op].call(n,d,g,e)).test)throw new t.JsonPatchError("Test operation failed","TEST_OPERATION_FAILED",0,n,e);return c}d=d[g]}}function f(e,n,r,o){if(void 0===o&&(o=!0),r&&!Array.isArray(n))throw new t.JsonPatchError("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");o||(e=a._deepClone(e));for(var i=new Array(n.length),u=0,s=n.length;u0)throw new t.JsonPatchError('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",n,e,r);if(("move"===e.op||"copy"===e.op)&&"string"!=typeof e.from)throw new t.JsonPatchError("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",n,e,r);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&void 0===e.value)throw new t.JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",n,e,r);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&a.hasUndefined(e.value))throw new t.JsonPatchError("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",n,e,r);if(r)if("add"==e.op){var i=e.path.split("/").length,s=o.split("/").length;if(i!==s+1&&i!==s)throw new t.JsonPatchError("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",n,e,r)}else if("replace"===e.op||"remove"===e.op||"_get"===e.op){if(e.path!==o)throw new t.JsonPatchError("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",n,e,r)}else if("move"===e.op||"copy"===e.op){var l=d([{op:"_get",path:e.from,value:void 0}],r);if(l&&"OPERATION_PATH_UNRESOLVABLE"===l.name)throw new t.JsonPatchError("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",n,e,r)}}function d(e,n,r){try{if(!Array.isArray(e))throw new t.JsonPatchError("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(n)f(a._deepClone(n),a._deepClone(e),r||!0);else{r=r||p;for(var o=0;o1&&void 0!==arguments[1]?arguments[1]:(0,a.List)();return function(e){return(e.authSelectors.definitionsToAuthorize()||(0,a.List)()).filter(function(e){return t.some(function(t){return t.get(e.keySeq().first())})})}},t.authorized=(0,i.createSelector)(s,function(e){return e.get("authorized")||(0,a.Map)()}),t.isAuthorized=function(e,t){return function(e){var n=e.authSelectors.authorized();return a.List.isList(t)?!!t.toJS().filter(function(e){return-1===(0,r.default)(e).map(function(e){return!!n.get(e)}).indexOf(!1)}).length:null}},t.getConfigs=(0,i.createSelector)(s,function(e){return e.get("configs")})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.execute=void 0;var r,o=n(25),i=(r=o)&&r.__esModule?r:{default:r};t.execute=function(e,t){var n=t.authSelectors,r=t.specSelectors;return function(t){var o=t.path,a=t.method,u=t.operation,s=t.extras,l={authorized:n.authorized()&&n.authorized().toJS(),definitions:r.securityDefinitions()&&r.securityDefinitions().toJS(),specSecurity:r.security()&&r.security().toJS()};return e((0,i.default)({path:o,method:a,operation:u,securities:l},s))}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{fn:{shallowEqualKeys:r.shallowEqualKeys}}};var r=n(9)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=s(n(41)),o=s(n(23));t.default=function(e){var t=e.fn,n={download:function(e){return function(n){var r=n.errActions,i=n.specSelectors,a=n.specActions,s=n.getConfigs,l=t.fetch,c=s();function f(t){if(t instanceof Error||t.status>=400)return a.updateLoadingStatus("failed"),r.newThrownErr((0,o.default)(new Error((t.message||t.statusText)+" "+e),{source:"fetch"})),void(!t.status&&t instanceof Error&&function(){try{var t=void 0;if("URL"in u.default?t=new URL(e):(t=document.createElement("a")).href=e,"https:"!==t.protocol&&"https:"===u.default.location.protocol){var n=(0,o.default)(new Error("Possible mixed-content issue? The page was loaded over https:// but a "+t.protocol+"// URL was specified. Check that you are not attempting to load mixed content."),{source:"fetch"});return void r.newThrownErr(n)}if(t.origin!==u.default.location.origin){var i=(0,o.default)(new Error("Possible cross-origin (CORS) issue? The URL origin ("+t.origin+") does not match the page ("+u.default.location.origin+"). Check the server returns the correct 'Access-Control-Allow-*' headers."),{source:"fetch"});r.newThrownErr(i)}}catch(e){return}}());a.updateLoadingStatus("success"),a.updateSpec(t.text),i.url()!==e&&a.updateUrl(e)}e=e||i.url(),a.updateLoadingStatus("loading"),r.clear({source:"fetch"}),l({url:e,loadSpec:!0,requestInterceptor:c.requestInterceptor||function(e){return e},responseInterceptor:c.responseInterceptor||function(e){return e},credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(f,f)}},updateLoadingStatus:function(e){var t=[null,"loading","failed","success","failedConfig"];return-1===t.indexOf(e)&&console.error("Error: "+e+" is not one of "+(0,r.default)(t)),{type:"spec_update_loading_status",payload:e}}},s={loadingStatus:(0,i.createSelector)(function(e){return e||(0,a.Map)()},function(e){return e.get("loadingStatus")||null})};return{statePlugins:{spec:{actions:n,reducers:{spec_update_loading_status:function(e,t){return"string"==typeof t.payload?e.set("loadingStatus",t.payload):e}},selectors:s}}}};var i=n(58),a=n(7),u=s(n(32));function s(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return{statePlugins:{spec:{actions:a,selectors:f},configs:{reducers:s.default,actions:i,selectors:u}}}};var r=c(n(934)),o=n(234),i=l(n(235)),a=l(n(401)),u=l(n(402)),s=c(n(403));function l(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}function c(e){return e&&e.__esModule?e:{default:e}}var f={getLocalConfig:function(){return(0,o.parseYamlConfig)(r.default)}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getConfigByUrl=t.downloadConfig=void 0;var r=n(234);t.downloadConfig=function(e){return function(t){return(0,t.fn.fetch)(e)}},t.getConfigByUrl=function(e,t){return function(n){var o=n.specActions;if(e)return o.downloadConfig(e).then(i,i);function i(n){n instanceof Error||n.status>=400?(o.updateLoadingStatus("failedConfig"),o.updateLoadingStatus("failedConfig"),o.updateUrl(""),console.error(n.statusText+" "+e.url),t(null)):t((0,r.parseYamlConfig)(n.text))}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.get=function(e,t){return e.getIn(Array.isArray(t)?t:[t])}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,o,i=n(22),a=(r=i)&&r.__esModule?r:{default:r},u=n(7),s=n(235);t.default=(o={},(0,a.default)(o,s.UPDATE_CONFIGS,function(e,t){return e.merge((0,u.fromJS)(t.payload))}),(0,a.default)(o,s.TOGGLE_CONFIGS,function(e,t){var n=t.payload,r=e.get(n);return e.set(n,!r)}),o)},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){return[r.default,{statePlugins:{configs:{wrapActions:{loaded:function(e,t){return function(){e.apply(void 0,arguments);var n=decodeURIComponent(window.location.hash);t.layoutActions.parseDeepLinkHash(n)}}}}},wrapComponents:{operation:o.default,OperationTag:i.default}}]};var r=a(n(405)),o=a(n(407)),i=a(n(408));function a(e){return e&&e.__esModule?e:{default:e}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.clearScrollTo=t.scrollToElement=t.readyToScroll=t.parseDeepLinkHash=t.scrollTo=t.show=void 0;var r,o=f(n(22)),i=f(n(17)),a=n(406),u=f(n(935)),s=n(9),l=n(7),c=f(l);function f(e){return e&&e.__esModule?e:{default:e}}var p=t.show=function(e,t){var n=t.getConfigs,r=t.layoutSelectors;return function(){for(var t=arguments.length,o=Array(t),u=0;u-1&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),n.show(h.map(function(e){return e.replace(/_/g," ")}),!0)),n.show(h,!0)}(f.indexOf("_")>-1||d.indexOf("_")>-1)&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),n.show(u.map(function(e){return e.replace(/_/g," ")}),!0)),n.show(u,!0),n.scrollTo(u)}}},v=t.readyToScroll=function(e,t){return function(n){var r=n.layoutSelectors.getScrollToKey();c.default.is(r,(0,l.fromJS)(e))&&(n.layoutActions.scrollToElement(t),n.layoutActions.clearScrollTo())}},m=t.scrollToElement=function(e,t){return function(n){try{t=t||n.fn.getScrollParent(e),u.default.createScroller(t).to(e)}catch(e){console.error(e)}}},g=t.clearScrollTo=function(){return{type:"layout_clear_scroll"}};t.default={fn:{getScrollParent:function(e,t){var n=document.documentElement,r=getComputedStyle(e),o="absolute"===r.position,i=t?/(auto|scroll|hidden)/:/(auto|scroll)/;if("fixed"===r.position)return n;for(var a=e;a=a.parentElement;)if(r=getComputedStyle(a),(!o||"static"!==r.position)&&i.test(r.overflow+r.overflowY+r.overflowX))return a;return n}},statePlugins:{layout:{actions:{scrollToElement:m,scrollTo:d,clearScrollTo:g,readyToScroll:v,parseDeepLinkHash:h},selectors:{getScrollToKey:function(e){return e.get("scrollToKey")},isShownKeyFromUrlHashArray:function(e,t){var n=(0,i.default)(t,2),r=n[0],o=n[1];return o?["operations",r,o]:r?["operations-tag",r]:[]},urlHashArrayFromIsShownKey:function(e,t){var n=(0,i.default)(t,3),r=n[0],o=n[1],a=n[2];return"operations"==r?[o,a]:"operations-tag"==r?[o]:[]}},reducers:(r={},(0,o.default)(r,"layout_scroll_to",function(e,t){return e.set("scrollToKey",c.default.fromJS(t.payload))}),(0,o.default)(r,"layout_clear_scroll",function(e){return e.delete("scrollToKey")}),r),wrapActions:{show:p}}}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.setHash=function(e){return e?history.pushState(null,null,"#"+e):window.location.hash=""}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=l(n(4)),o=l(n(2)),i=l(n(3)),a=l(n(5)),u=l(n(6)),s=l(n(0));l(n(12));function l(e){return e&&e.__esModule?e:{default:e}}t.default=function(e,t){return function(n){function l(){var e,n,i,u;(0,o.default)(this,l);for(var s=arguments.length,c=Array(s),f=0;f",Gt:"≫",gt:">",gtcc:"⪧",gtcir:"⩺",gtdot:"⋗",gtlPar:"⦕",gtquest:"⩼",gtrapprox:"⪆",gtrarr:"⥸",gtrdot:"⋗",gtreqless:"⋛",gtreqqless:"⪌",gtrless:"≷",gtrsim:"≳",gvertneqq:"≩︀",gvnE:"≩︀",Hacek:"ˇ",hairsp:" ",half:"½",hamilt:"ℋ",HARDcy:"Ъ",hardcy:"ъ",hArr:"⇔",harr:"↔",harrcir:"⥈",harrw:"↭",Hat:"^",hbar:"ℏ",Hcirc:"Ĥ",hcirc:"ĥ",hearts:"♥",heartsuit:"♥",hellip:"…",hercon:"⊹",Hfr:"ℌ",hfr:"𝔥",HilbertSpace:"ℋ",hksearow:"⤥",hkswarow:"⤦",hoarr:"⇿",homtht:"∻",hookleftarrow:"↩",hookrightarrow:"↪",Hopf:"ℍ",hopf:"𝕙",horbar:"―",HorizontalLine:"─",Hscr:"ℋ",hscr:"𝒽",hslash:"ℏ",Hstrok:"Ħ",hstrok:"ħ",HumpDownHump:"≎",HumpEqual:"≏",hybull:"⁃",hyphen:"‐",Iacute:"Í",iacute:"í",ic:"⁣",Icirc:"Î",icirc:"î",Icy:"И",icy:"и",Idot:"İ",IEcy:"Е",iecy:"е",iexcl:"¡",iff:"⇔",Ifr:"ℑ",ifr:"𝔦",Igrave:"Ì",igrave:"ì",ii:"ⅈ",iiiint:"⨌",iiint:"∭",iinfin:"⧜",iiota:"℩",IJlig:"IJ",ijlig:"ij",Im:"ℑ",Imacr:"Ī",imacr:"ī",image:"ℑ",ImaginaryI:"ⅈ",imagline:"ℐ",imagpart:"ℑ",imath:"ı",imof:"⊷",imped:"Ƶ",Implies:"⇒",in:"∈",incare:"℅",infin:"∞",infintie:"⧝",inodot:"ı",Int:"∬",int:"∫",intcal:"⊺",integers:"ℤ",Integral:"∫",intercal:"⊺",Intersection:"⋂",intlarhk:"⨗",intprod:"⨼",InvisibleComma:"⁣",InvisibleTimes:"⁢",IOcy:"Ё",iocy:"ё",Iogon:"Į",iogon:"į",Iopf:"𝕀",iopf:"𝕚",Iota:"Ι",iota:"ι",iprod:"⨼",iquest:"¿",Iscr:"ℐ",iscr:"𝒾",isin:"∈",isindot:"⋵",isinE:"⋹",isins:"⋴",isinsv:"⋳",isinv:"∈",it:"⁢",Itilde:"Ĩ",itilde:"ĩ",Iukcy:"І",iukcy:"і",Iuml:"Ï",iuml:"ï",Jcirc:"Ĵ",jcirc:"ĵ",Jcy:"Й",jcy:"й",Jfr:"𝔍",jfr:"𝔧",jmath:"ȷ",Jopf:"𝕁",jopf:"𝕛",Jscr:"𝒥",jscr:"𝒿",Jsercy:"Ј",jsercy:"ј",Jukcy:"Є",jukcy:"є",Kappa:"Κ",kappa:"κ",kappav:"ϰ",Kcedil:"Ķ",kcedil:"ķ",Kcy:"К",kcy:"к",Kfr:"𝔎",kfr:"𝔨",kgreen:"ĸ",KHcy:"Х",khcy:"х",KJcy:"Ќ",kjcy:"ќ",Kopf:"𝕂",kopf:"𝕜",Kscr:"𝒦",kscr:"𝓀",lAarr:"⇚",Lacute:"Ĺ",lacute:"ĺ",laemptyv:"⦴",lagran:"ℒ",Lambda:"Λ",lambda:"λ",Lang:"⟪",lang:"⟨",langd:"⦑",langle:"⟨",lap:"⪅",Laplacetrf:"ℒ",laquo:"«",Larr:"↞",lArr:"⇐",larr:"←",larrb:"⇤",larrbfs:"⤟",larrfs:"⤝",larrhk:"↩",larrlp:"↫",larrpl:"⤹",larrsim:"⥳",larrtl:"↢",lat:"⪫",lAtail:"⤛",latail:"⤙",late:"⪭",lates:"⪭︀",lBarr:"⤎",lbarr:"⤌",lbbrk:"❲",lbrace:"{",lbrack:"[",lbrke:"⦋",lbrksld:"⦏",lbrkslu:"⦍",Lcaron:"Ľ",lcaron:"ľ",Lcedil:"Ļ",lcedil:"ļ",lceil:"⌈",lcub:"{",Lcy:"Л",lcy:"л",ldca:"⤶",ldquo:"“",ldquor:"„",ldrdhar:"⥧",ldrushar:"⥋",ldsh:"↲",lE:"≦",le:"≤",LeftAngleBracket:"⟨",LeftArrow:"←",Leftarrow:"⇐",leftarrow:"←",LeftArrowBar:"⇤",LeftArrowRightArrow:"⇆",leftarrowtail:"↢",LeftCeiling:"⌈",LeftDoubleBracket:"⟦",LeftDownTeeVector:"⥡",LeftDownVector:"⇃",LeftDownVectorBar:"⥙",LeftFloor:"⌊",leftharpoondown:"↽",leftharpoonup:"↼",leftleftarrows:"⇇",LeftRightArrow:"↔",Leftrightarrow:"⇔",leftrightarrow:"↔",leftrightarrows:"⇆",leftrightharpoons:"⇋",leftrightsquigarrow:"↭",LeftRightVector:"⥎",LeftTee:"⊣",LeftTeeArrow:"↤",LeftTeeVector:"⥚",leftthreetimes:"⋋",LeftTriangle:"⊲",LeftTriangleBar:"⧏",LeftTriangleEqual:"⊴",LeftUpDownVector:"⥑",LeftUpTeeVector:"⥠",LeftUpVector:"↿",LeftUpVectorBar:"⥘",LeftVector:"↼",LeftVectorBar:"⥒",lEg:"⪋",leg:"⋚",leq:"≤",leqq:"≦",leqslant:"⩽",les:"⩽",lescc:"⪨",lesdot:"⩿",lesdoto:"⪁",lesdotor:"⪃",lesg:"⋚︀",lesges:"⪓",lessapprox:"⪅",lessdot:"⋖",lesseqgtr:"⋚",lesseqqgtr:"⪋",LessEqualGreater:"⋚",LessFullEqual:"≦",LessGreater:"≶",lessgtr:"≶",LessLess:"⪡",lesssim:"≲",LessSlantEqual:"⩽",LessTilde:"≲",lfisht:"⥼",lfloor:"⌊",Lfr:"𝔏",lfr:"𝔩",lg:"≶",lgE:"⪑",lHar:"⥢",lhard:"↽",lharu:"↼",lharul:"⥪",lhblk:"▄",LJcy:"Љ",ljcy:"љ",Ll:"⋘",ll:"≪",llarr:"⇇",llcorner:"⌞",Lleftarrow:"⇚",llhard:"⥫",lltri:"◺",Lmidot:"Ŀ",lmidot:"ŀ",lmoust:"⎰",lmoustache:"⎰",lnap:"⪉",lnapprox:"⪉",lnE:"≨",lne:"⪇",lneq:"⪇",lneqq:"≨",lnsim:"⋦",loang:"⟬",loarr:"⇽",lobrk:"⟦",LongLeftArrow:"⟵",Longleftarrow:"⟸",longleftarrow:"⟵",LongLeftRightArrow:"⟷",Longleftrightarrow:"⟺",longleftrightarrow:"⟷",longmapsto:"⟼",LongRightArrow:"⟶",Longrightarrow:"⟹",longrightarrow:"⟶",looparrowleft:"↫",looparrowright:"↬",lopar:"⦅",Lopf:"𝕃",lopf:"𝕝",loplus:"⨭",lotimes:"⨴",lowast:"∗",lowbar:"_",LowerLeftArrow:"↙",LowerRightArrow:"↘",loz:"◊",lozenge:"◊",lozf:"⧫",lpar:"(",lparlt:"⦓",lrarr:"⇆",lrcorner:"⌟",lrhar:"⇋",lrhard:"⥭",lrm:"‎",lrtri:"⊿",lsaquo:"‹",Lscr:"ℒ",lscr:"𝓁",Lsh:"↰",lsh:"↰",lsim:"≲",lsime:"⪍",lsimg:"⪏",lsqb:"[",lsquo:"‘",lsquor:"‚",Lstrok:"Ł",lstrok:"ł",LT:"<",Lt:"≪",lt:"<",ltcc:"⪦",ltcir:"⩹",ltdot:"⋖",lthree:"⋋",ltimes:"⋉",ltlarr:"⥶",ltquest:"⩻",ltri:"◃",ltrie:"⊴",ltrif:"◂",ltrPar:"⦖",lurdshar:"⥊",luruhar:"⥦",lvertneqq:"≨︀",lvnE:"≨︀",macr:"¯",male:"♂",malt:"✠",maltese:"✠",Map:"⤅",map:"↦",mapsto:"↦",mapstodown:"↧",mapstoleft:"↤",mapstoup:"↥",marker:"▮",mcomma:"⨩",Mcy:"М",mcy:"м",mdash:"—",mDDot:"∺",measuredangle:"∡",MediumSpace:" ",Mellintrf:"ℳ",Mfr:"𝔐",mfr:"𝔪",mho:"℧",micro:"µ",mid:"∣",midast:"*",midcir:"⫰",middot:"·",minus:"−",minusb:"⊟",minusd:"∸",minusdu:"⨪",MinusPlus:"∓",mlcp:"⫛",mldr:"…",mnplus:"∓",models:"⊧",Mopf:"𝕄",mopf:"𝕞",mp:"∓",Mscr:"ℳ",mscr:"𝓂",mstpos:"∾",Mu:"Μ",mu:"μ",multimap:"⊸",mumap:"⊸",nabla:"∇",Nacute:"Ń",nacute:"ń",nang:"∠⃒",nap:"≉",napE:"⩰̸",napid:"≋̸",napos:"ʼn",napprox:"≉",natur:"♮",natural:"♮",naturals:"ℕ",nbsp:" ",nbump:"≎̸",nbumpe:"≏̸",ncap:"⩃",Ncaron:"Ň",ncaron:"ň",Ncedil:"Ņ",ncedil:"ņ",ncong:"≇",ncongdot:"⩭̸",ncup:"⩂",Ncy:"Н",ncy:"н",ndash:"–",ne:"≠",nearhk:"⤤",neArr:"⇗",nearr:"↗",nearrow:"↗",nedot:"≐̸",NegativeMediumSpace:"​",NegativeThickSpace:"​",NegativeThinSpace:"​",NegativeVeryThinSpace:"​",nequiv:"≢",nesear:"⤨",nesim:"≂̸",NestedGreaterGreater:"≫",NestedLessLess:"≪",NewLine:"\n",nexist:"∄",nexists:"∄",Nfr:"𝔑",nfr:"𝔫",ngE:"≧̸",nge:"≱",ngeq:"≱",ngeqq:"≧̸",ngeqslant:"⩾̸",nges:"⩾̸",nGg:"⋙̸",ngsim:"≵",nGt:"≫⃒",ngt:"≯",ngtr:"≯",nGtv:"≫̸",nhArr:"⇎",nharr:"↮",nhpar:"⫲",ni:"∋",nis:"⋼",nisd:"⋺",niv:"∋",NJcy:"Њ",njcy:"њ",nlArr:"⇍",nlarr:"↚",nldr:"‥",nlE:"≦̸",nle:"≰",nLeftarrow:"⇍",nleftarrow:"↚",nLeftrightarrow:"⇎",nleftrightarrow:"↮",nleq:"≰",nleqq:"≦̸",nleqslant:"⩽̸",nles:"⩽̸",nless:"≮",nLl:"⋘̸",nlsim:"≴",nLt:"≪⃒",nlt:"≮",nltri:"⋪",nltrie:"⋬",nLtv:"≪̸",nmid:"∤",NoBreak:"⁠",NonBreakingSpace:" ",Nopf:"ℕ",nopf:"𝕟",Not:"⫬",not:"¬",NotCongruent:"≢",NotCupCap:"≭",NotDoubleVerticalBar:"∦",NotElement:"∉",NotEqual:"≠",NotEqualTilde:"≂̸",NotExists:"∄",NotGreater:"≯",NotGreaterEqual:"≱",NotGreaterFullEqual:"≧̸",NotGreaterGreater:"≫̸",NotGreaterLess:"≹",NotGreaterSlantEqual:"⩾̸",NotGreaterTilde:"≵",NotHumpDownHump:"≎̸",NotHumpEqual:"≏̸",notin:"∉",notindot:"⋵̸",notinE:"⋹̸",notinva:"∉",notinvb:"⋷",notinvc:"⋶",NotLeftTriangle:"⋪",NotLeftTriangleBar:"⧏̸",NotLeftTriangleEqual:"⋬",NotLess:"≮",NotLessEqual:"≰",NotLessGreater:"≸",NotLessLess:"≪̸",NotLessSlantEqual:"⩽̸",NotLessTilde:"≴",NotNestedGreaterGreater:"⪢̸",NotNestedLessLess:"⪡̸",notni:"∌",notniva:"∌",notnivb:"⋾",notnivc:"⋽",NotPrecedes:"⊀",NotPrecedesEqual:"⪯̸",NotPrecedesSlantEqual:"⋠",NotReverseElement:"∌",NotRightTriangle:"⋫",NotRightTriangleBar:"⧐̸",NotRightTriangleEqual:"⋭",NotSquareSubset:"⊏̸",NotSquareSubsetEqual:"⋢",NotSquareSuperset:"⊐̸",NotSquareSupersetEqual:"⋣",NotSubset:"⊂⃒",NotSubsetEqual:"⊈",NotSucceeds:"⊁",NotSucceedsEqual:"⪰̸",NotSucceedsSlantEqual:"⋡",NotSucceedsTilde:"≿̸",NotSuperset:"⊃⃒",NotSupersetEqual:"⊉",NotTilde:"≁",NotTildeEqual:"≄",NotTildeFullEqual:"≇",NotTildeTilde:"≉",NotVerticalBar:"∤",npar:"∦",nparallel:"∦",nparsl:"⫽⃥",npart:"∂̸",npolint:"⨔",npr:"⊀",nprcue:"⋠",npre:"⪯̸",nprec:"⊀",npreceq:"⪯̸",nrArr:"⇏",nrarr:"↛",nrarrc:"⤳̸",nrarrw:"↝̸",nRightarrow:"⇏",nrightarrow:"↛",nrtri:"⋫",nrtrie:"⋭",nsc:"⊁",nsccue:"⋡",nsce:"⪰̸",Nscr:"𝒩",nscr:"𝓃",nshortmid:"∤",nshortparallel:"∦",nsim:"≁",nsime:"≄",nsimeq:"≄",nsmid:"∤",nspar:"∦",nsqsube:"⋢",nsqsupe:"⋣",nsub:"⊄",nsubE:"⫅̸",nsube:"⊈",nsubset:"⊂⃒",nsubseteq:"⊈",nsubseteqq:"⫅̸",nsucc:"⊁",nsucceq:"⪰̸",nsup:"⊅",nsupE:"⫆̸",nsupe:"⊉",nsupset:"⊃⃒",nsupseteq:"⊉",nsupseteqq:"⫆̸",ntgl:"≹",Ntilde:"Ñ",ntilde:"ñ",ntlg:"≸",ntriangleleft:"⋪",ntrianglelefteq:"⋬",ntriangleright:"⋫",ntrianglerighteq:"⋭",Nu:"Ν",nu:"ν",num:"#",numero:"№",numsp:" ",nvap:"≍⃒",nVDash:"⊯",nVdash:"⊮",nvDash:"⊭",nvdash:"⊬",nvge:"≥⃒",nvgt:">⃒",nvHarr:"⤄",nvinfin:"⧞",nvlArr:"⤂",nvle:"≤⃒",nvlt:"<⃒",nvltrie:"⊴⃒",nvrArr:"⤃",nvrtrie:"⊵⃒",nvsim:"∼⃒",nwarhk:"⤣",nwArr:"⇖",nwarr:"↖",nwarrow:"↖",nwnear:"⤧",Oacute:"Ó",oacute:"ó",oast:"⊛",ocir:"⊚",Ocirc:"Ô",ocirc:"ô",Ocy:"О",ocy:"о",odash:"⊝",Odblac:"Ő",odblac:"ő",odiv:"⨸",odot:"⊙",odsold:"⦼",OElig:"Œ",oelig:"œ",ofcir:"⦿",Ofr:"𝔒",ofr:"𝔬",ogon:"˛",Ograve:"Ò",ograve:"ò",ogt:"⧁",ohbar:"⦵",ohm:"Ω",oint:"∮",olarr:"↺",olcir:"⦾",olcross:"⦻",oline:"‾",olt:"⧀",Omacr:"Ō",omacr:"ō",Omega:"Ω",omega:"ω",Omicron:"Ο",omicron:"ο",omid:"⦶",ominus:"⊖",Oopf:"𝕆",oopf:"𝕠",opar:"⦷",OpenCurlyDoubleQuote:"“",OpenCurlyQuote:"‘",operp:"⦹",oplus:"⊕",Or:"⩔",or:"∨",orarr:"↻",ord:"⩝",order:"ℴ",orderof:"ℴ",ordf:"ª",ordm:"º",origof:"⊶",oror:"⩖",orslope:"⩗",orv:"⩛",oS:"Ⓢ",Oscr:"𝒪",oscr:"ℴ",Oslash:"Ø",oslash:"ø",osol:"⊘",Otilde:"Õ",otilde:"õ",Otimes:"⨷",otimes:"⊗",otimesas:"⨶",Ouml:"Ö",ouml:"ö",ovbar:"⌽",OverBar:"‾",OverBrace:"⏞",OverBracket:"⎴",OverParenthesis:"⏜",par:"∥",para:"¶",parallel:"∥",parsim:"⫳",parsl:"⫽",part:"∂",PartialD:"∂",Pcy:"П",pcy:"п",percnt:"%",period:".",permil:"‰",perp:"⊥",pertenk:"‱",Pfr:"𝔓",pfr:"𝔭",Phi:"Φ",phi:"φ",phiv:"ϕ",phmmat:"ℳ",phone:"☎",Pi:"Π",pi:"π",pitchfork:"⋔",piv:"ϖ",planck:"ℏ",planckh:"ℎ",plankv:"ℏ",plus:"+",plusacir:"⨣",plusb:"⊞",pluscir:"⨢",plusdo:"∔",plusdu:"⨥",pluse:"⩲",PlusMinus:"±",plusmn:"±",plussim:"⨦",plustwo:"⨧",pm:"±",Poincareplane:"ℌ",pointint:"⨕",Popf:"ℙ",popf:"𝕡",pound:"£",Pr:"⪻",pr:"≺",prap:"⪷",prcue:"≼",prE:"⪳",pre:"⪯",prec:"≺",precapprox:"⪷",preccurlyeq:"≼",Precedes:"≺",PrecedesEqual:"⪯",PrecedesSlantEqual:"≼",PrecedesTilde:"≾",preceq:"⪯",precnapprox:"⪹",precneqq:"⪵",precnsim:"⋨",precsim:"≾",Prime:"″",prime:"′",primes:"ℙ",prnap:"⪹",prnE:"⪵",prnsim:"⋨",prod:"∏",Product:"∏",profalar:"⌮",profline:"⌒",profsurf:"⌓",prop:"∝",Proportion:"∷",Proportional:"∝",propto:"∝",prsim:"≾",prurel:"⊰",Pscr:"𝒫",pscr:"𝓅",Psi:"Ψ",psi:"ψ",puncsp:" ",Qfr:"𝔔",qfr:"𝔮",qint:"⨌",Qopf:"ℚ",qopf:"𝕢",qprime:"⁗",Qscr:"𝒬",qscr:"𝓆",quaternions:"ℍ",quatint:"⨖",quest:"?",questeq:"≟",QUOT:'"',quot:'"',rAarr:"⇛",race:"∽̱",Racute:"Ŕ",racute:"ŕ",radic:"√",raemptyv:"⦳",Rang:"⟫",rang:"⟩",rangd:"⦒",range:"⦥",rangle:"⟩",raquo:"»",Rarr:"↠",rArr:"⇒",rarr:"→",rarrap:"⥵",rarrb:"⇥",rarrbfs:"⤠",rarrc:"⤳",rarrfs:"⤞",rarrhk:"↪",rarrlp:"↬",rarrpl:"⥅",rarrsim:"⥴",Rarrtl:"⤖",rarrtl:"↣",rarrw:"↝",rAtail:"⤜",ratail:"⤚",ratio:"∶",rationals:"ℚ",RBarr:"⤐",rBarr:"⤏",rbarr:"⤍",rbbrk:"❳",rbrace:"}",rbrack:"]",rbrke:"⦌",rbrksld:"⦎",rbrkslu:"⦐",Rcaron:"Ř",rcaron:"ř",Rcedil:"Ŗ",rcedil:"ŗ",rceil:"⌉",rcub:"}",Rcy:"Р",rcy:"р",rdca:"⤷",rdldhar:"⥩",rdquo:"”",rdquor:"”",rdsh:"↳",Re:"ℜ",real:"ℜ",realine:"ℛ",realpart:"ℜ",reals:"ℝ",rect:"▭",REG:"®",reg:"®",ReverseElement:"∋",ReverseEquilibrium:"⇋",ReverseUpEquilibrium:"⥯",rfisht:"⥽",rfloor:"⌋",Rfr:"ℜ",rfr:"𝔯",rHar:"⥤",rhard:"⇁",rharu:"⇀",rharul:"⥬",Rho:"Ρ",rho:"ρ",rhov:"ϱ",RightAngleBracket:"⟩",RightArrow:"→",Rightarrow:"⇒",rightarrow:"→",RightArrowBar:"⇥",RightArrowLeftArrow:"⇄",rightarrowtail:"↣",RightCeiling:"⌉",RightDoubleBracket:"⟧",RightDownTeeVector:"⥝",RightDownVector:"⇂",RightDownVectorBar:"⥕",RightFloor:"⌋",rightharpoondown:"⇁",rightharpoonup:"⇀",rightleftarrows:"⇄",rightleftharpoons:"⇌",rightrightarrows:"⇉",rightsquigarrow:"↝",RightTee:"⊢",RightTeeArrow:"↦",RightTeeVector:"⥛",rightthreetimes:"⋌",RightTriangle:"⊳",RightTriangleBar:"⧐",RightTriangleEqual:"⊵",RightUpDownVector:"⥏",RightUpTeeVector:"⥜",RightUpVector:"↾",RightUpVectorBar:"⥔",RightVector:"⇀",RightVectorBar:"⥓",ring:"˚",risingdotseq:"≓",rlarr:"⇄",rlhar:"⇌",rlm:"‏",rmoust:"⎱",rmoustache:"⎱",rnmid:"⫮",roang:"⟭",roarr:"⇾",robrk:"⟧",ropar:"⦆",Ropf:"ℝ",ropf:"𝕣",roplus:"⨮",rotimes:"⨵",RoundImplies:"⥰",rpar:")",rpargt:"⦔",rppolint:"⨒",rrarr:"⇉",Rrightarrow:"⇛",rsaquo:"›",Rscr:"ℛ",rscr:"𝓇",Rsh:"↱",rsh:"↱",rsqb:"]",rsquo:"’",rsquor:"’",rthree:"⋌",rtimes:"⋊",rtri:"▹",rtrie:"⊵",rtrif:"▸",rtriltri:"⧎",RuleDelayed:"⧴",ruluhar:"⥨",rx:"℞",Sacute:"Ś",sacute:"ś",sbquo:"‚",Sc:"⪼",sc:"≻",scap:"⪸",Scaron:"Š",scaron:"š",sccue:"≽",scE:"⪴",sce:"⪰",Scedil:"Ş",scedil:"ş",Scirc:"Ŝ",scirc:"ŝ",scnap:"⪺",scnE:"⪶",scnsim:"⋩",scpolint:"⨓",scsim:"≿",Scy:"С",scy:"с",sdot:"⋅",sdotb:"⊡",sdote:"⩦",searhk:"⤥",seArr:"⇘",searr:"↘",searrow:"↘",sect:"§",semi:";",seswar:"⤩",setminus:"∖",setmn:"∖",sext:"✶",Sfr:"𝔖",sfr:"𝔰",sfrown:"⌢",sharp:"♯",SHCHcy:"Щ",shchcy:"щ",SHcy:"Ш",shcy:"ш",ShortDownArrow:"↓",ShortLeftArrow:"←",shortmid:"∣",shortparallel:"∥",ShortRightArrow:"→",ShortUpArrow:"↑",shy:"­",Sigma:"Σ",sigma:"σ",sigmaf:"ς",sigmav:"ς",sim:"∼",simdot:"⩪",sime:"≃",simeq:"≃",simg:"⪞",simgE:"⪠",siml:"⪝",simlE:"⪟",simne:"≆",simplus:"⨤",simrarr:"⥲",slarr:"←",SmallCircle:"∘",smallsetminus:"∖",smashp:"⨳",smeparsl:"⧤",smid:"∣",smile:"⌣",smt:"⪪",smte:"⪬",smtes:"⪬︀",SOFTcy:"Ь",softcy:"ь",sol:"/",solb:"⧄",solbar:"⌿",Sopf:"𝕊",sopf:"𝕤",spades:"♠",spadesuit:"♠",spar:"∥",sqcap:"⊓",sqcaps:"⊓︀",sqcup:"⊔",sqcups:"⊔︀",Sqrt:"√",sqsub:"⊏",sqsube:"⊑",sqsubset:"⊏",sqsubseteq:"⊑",sqsup:"⊐",sqsupe:"⊒",sqsupset:"⊐",sqsupseteq:"⊒",squ:"□",Square:"□",square:"□",SquareIntersection:"⊓",SquareSubset:"⊏",SquareSubsetEqual:"⊑",SquareSuperset:"⊐",SquareSupersetEqual:"⊒",SquareUnion:"⊔",squarf:"▪",squf:"▪",srarr:"→",Sscr:"𝒮",sscr:"𝓈",ssetmn:"∖",ssmile:"⌣",sstarf:"⋆",Star:"⋆",star:"☆",starf:"★",straightepsilon:"ϵ",straightphi:"ϕ",strns:"¯",Sub:"⋐",sub:"⊂",subdot:"⪽",subE:"⫅",sube:"⊆",subedot:"⫃",submult:"⫁",subnE:"⫋",subne:"⊊",subplus:"⪿",subrarr:"⥹",Subset:"⋐",subset:"⊂",subseteq:"⊆",subseteqq:"⫅",SubsetEqual:"⊆",subsetneq:"⊊",subsetneqq:"⫋",subsim:"⫇",subsub:"⫕",subsup:"⫓",succ:"≻",succapprox:"⪸",succcurlyeq:"≽",Succeeds:"≻",SucceedsEqual:"⪰",SucceedsSlantEqual:"≽",SucceedsTilde:"≿",succeq:"⪰",succnapprox:"⪺",succneqq:"⪶",succnsim:"⋩",succsim:"≿",SuchThat:"∋",Sum:"∑",sum:"∑",sung:"♪",Sup:"⋑",sup:"⊃",sup1:"¹",sup2:"²",sup3:"³",supdot:"⪾",supdsub:"⫘",supE:"⫆",supe:"⊇",supedot:"⫄",Superset:"⊃",SupersetEqual:"⊇",suphsol:"⟉",suphsub:"⫗",suplarr:"⥻",supmult:"⫂",supnE:"⫌",supne:"⊋",supplus:"⫀",Supset:"⋑",supset:"⊃",supseteq:"⊇",supseteqq:"⫆",supsetneq:"⊋",supsetneqq:"⫌",supsim:"⫈",supsub:"⫔",supsup:"⫖",swarhk:"⤦",swArr:"⇙",swarr:"↙",swarrow:"↙",swnwar:"⤪",szlig:"ß",Tab:"\t",target:"⌖",Tau:"Τ",tau:"τ",tbrk:"⎴",Tcaron:"Ť",tcaron:"ť",Tcedil:"Ţ",tcedil:"ţ",Tcy:"Т",tcy:"т",tdot:"⃛",telrec:"⌕",Tfr:"𝔗",tfr:"𝔱",there4:"∴",Therefore:"∴",therefore:"∴",Theta:"Θ",theta:"θ",thetasym:"ϑ",thetav:"ϑ",thickapprox:"≈",thicksim:"∼",ThickSpace:"  ",thinsp:" ",ThinSpace:" ",thkap:"≈",thksim:"∼",THORN:"Þ",thorn:"þ",Tilde:"∼",tilde:"˜",TildeEqual:"≃",TildeFullEqual:"≅",TildeTilde:"≈",times:"×",timesb:"⊠",timesbar:"⨱",timesd:"⨰",tint:"∭",toea:"⤨",top:"⊤",topbot:"⌶",topcir:"⫱",Topf:"𝕋",topf:"𝕥",topfork:"⫚",tosa:"⤩",tprime:"‴",TRADE:"™",trade:"™",triangle:"▵",triangledown:"▿",triangleleft:"◃",trianglelefteq:"⊴",triangleq:"≜",triangleright:"▹",trianglerighteq:"⊵",tridot:"◬",trie:"≜",triminus:"⨺",TripleDot:"⃛",triplus:"⨹",trisb:"⧍",tritime:"⨻",trpezium:"⏢",Tscr:"𝒯",tscr:"𝓉",TScy:"Ц",tscy:"ц",TSHcy:"Ћ",tshcy:"ћ",Tstrok:"Ŧ",tstrok:"ŧ",twixt:"≬",twoheadleftarrow:"↞",twoheadrightarrow:"↠",Uacute:"Ú",uacute:"ú",Uarr:"↟",uArr:"⇑",uarr:"↑",Uarrocir:"⥉",Ubrcy:"Ў",ubrcy:"ў",Ubreve:"Ŭ",ubreve:"ŭ",Ucirc:"Û",ucirc:"û",Ucy:"У",ucy:"у",udarr:"⇅",Udblac:"Ű",udblac:"ű",udhar:"⥮",ufisht:"⥾",Ufr:"𝔘",ufr:"𝔲",Ugrave:"Ù",ugrave:"ù",uHar:"⥣",uharl:"↿",uharr:"↾",uhblk:"▀",ulcorn:"⌜",ulcorner:"⌜",ulcrop:"⌏",ultri:"◸",Umacr:"Ū",umacr:"ū",uml:"¨",UnderBar:"_",UnderBrace:"⏟",UnderBracket:"⎵",UnderParenthesis:"⏝",Union:"⋃",UnionPlus:"⊎",Uogon:"Ų",uogon:"ų",Uopf:"𝕌",uopf:"𝕦",UpArrow:"↑",Uparrow:"⇑",uparrow:"↑",UpArrowBar:"⤒",UpArrowDownArrow:"⇅",UpDownArrow:"↕",Updownarrow:"⇕",updownarrow:"↕",UpEquilibrium:"⥮",upharpoonleft:"↿",upharpoonright:"↾",uplus:"⊎",UpperLeftArrow:"↖",UpperRightArrow:"↗",Upsi:"ϒ",upsi:"υ",upsih:"ϒ",Upsilon:"Υ",upsilon:"υ",UpTee:"⊥",UpTeeArrow:"↥",upuparrows:"⇈",urcorn:"⌝",urcorner:"⌝",urcrop:"⌎",Uring:"Ů",uring:"ů",urtri:"◹",Uscr:"𝒰",uscr:"𝓊",utdot:"⋰",Utilde:"Ũ",utilde:"ũ",utri:"▵",utrif:"▴",uuarr:"⇈",Uuml:"Ü",uuml:"ü",uwangle:"⦧",vangrt:"⦜",varepsilon:"ϵ",varkappa:"ϰ",varnothing:"∅",varphi:"ϕ",varpi:"ϖ",varpropto:"∝",vArr:"⇕",varr:"↕",varrho:"ϱ",varsigma:"ς",varsubsetneq:"⊊︀",varsubsetneqq:"⫋︀",varsupsetneq:"⊋︀",varsupsetneqq:"⫌︀",vartheta:"ϑ",vartriangleleft:"⊲",vartriangleright:"⊳",Vbar:"⫫",vBar:"⫨",vBarv:"⫩",Vcy:"В",vcy:"в",VDash:"⊫",Vdash:"⊩",vDash:"⊨",vdash:"⊢",Vdashl:"⫦",Vee:"⋁",vee:"∨",veebar:"⊻",veeeq:"≚",vellip:"⋮",Verbar:"‖",verbar:"|",Vert:"‖",vert:"|",VerticalBar:"∣",VerticalLine:"|",VerticalSeparator:"❘",VerticalTilde:"≀",VeryThinSpace:" ",Vfr:"𝔙",vfr:"𝔳",vltri:"⊲",vnsub:"⊂⃒",vnsup:"⊃⃒",Vopf:"𝕍",vopf:"𝕧",vprop:"∝",vrtri:"⊳",Vscr:"𝒱",vscr:"𝓋",vsubnE:"⫋︀",vsubne:"⊊︀",vsupnE:"⫌︀",vsupne:"⊋︀",Vvdash:"⊪",vzigzag:"⦚",Wcirc:"Ŵ",wcirc:"ŵ",wedbar:"⩟",Wedge:"⋀",wedge:"∧",wedgeq:"≙",weierp:"℘",Wfr:"𝔚",wfr:"𝔴",Wopf:"𝕎",wopf:"𝕨",wp:"℘",wr:"≀",wreath:"≀",Wscr:"𝒲",wscr:"𝓌",xcap:"⋂",xcirc:"◯",xcup:"⋃",xdtri:"▽",Xfr:"𝔛",xfr:"𝔵",xhArr:"⟺",xharr:"⟷",Xi:"Ξ",xi:"ξ",xlArr:"⟸",xlarr:"⟵",xmap:"⟼",xnis:"⋻",xodot:"⨀",Xopf:"𝕏",xopf:"𝕩",xoplus:"⨁",xotime:"⨂",xrArr:"⟹",xrarr:"⟶",Xscr:"𝒳",xscr:"𝓍",xsqcup:"⨆",xuplus:"⨄",xutri:"△",xvee:"⋁",xwedge:"⋀",Yacute:"Ý",yacute:"ý",YAcy:"Я",yacy:"я",Ycirc:"Ŷ",ycirc:"ŷ",Ycy:"Ы",ycy:"ы",yen:"¥",Yfr:"𝔜",yfr:"𝔶",YIcy:"Ї",yicy:"ї",Yopf:"𝕐",yopf:"𝕪",Yscr:"𝒴",yscr:"𝓎",YUcy:"Ю",yucy:"ю",Yuml:"Ÿ",yuml:"ÿ",Zacute:"Ź",zacute:"ź",Zcaron:"Ž",zcaron:"ž",Zcy:"З",zcy:"з",Zdot:"Ż",zdot:"ż",zeetrf:"ℨ",ZeroWidthSpace:"​",Zeta:"Ζ",zeta:"ζ",Zfr:"ℨ",zfr:"𝔷",ZHcy:"Ж",zhcy:"ж",zigrarr:"⇝",Zopf:"ℤ",zopf:"𝕫",Zscr:"𝒵",zscr:"𝓏",zwj:"‍",zwnj:"‌"}},function(e,t,n){"use strict";var r=n(419),o=n(27).unescapeMd;e.exports=function(e,t){var n,i,a,u=t,s=e.posMax;if(60===e.src.charCodeAt(t)){for(t++;t8&&n<14);)if(92===n&&t+11)break;if(41===n&&--i<0)break;t++}return u!==t&&(a=o(e.src.slice(u,t)),!!e.parser.validateLink(a)&&(e.linkContent=a,e.pos=t,!0))}},function(e,t,n){"use strict";var r=n(27).replaceEntities;e.exports=function(e){var t=r(e);try{t=decodeURI(t)}catch(e){}return encodeURI(t)}},function(e,t,n){"use strict";var r=n(27).unescapeMd;e.exports=function(e,t){var n,o=t,i=e.posMax,a=e.src.charCodeAt(t);if(34!==a&&39!==a&&40!==a)return!1;for(t++,40===a&&(a=41);t1?r-1:0),i=1;i1?t-1:0),r=1;r0){var S=a("JsonSchemaForm"),C=a("ParameterExt"),k=w.get("properties",(0,o.OrderedMap)());return n=o.Map.isMap(n)?n:(0,o.OrderedMap)(),r.default.createElement("div",{className:"table-container"},y&&r.default.createElement(h,{source:y}),r.default.createElement("table",null,r.default.createElement("tbody",null,k.map(function(e,t){var u=g?(0,i.getCommonExtensions)(e):null,s=w.get("required",(0,o.List)()).includes(t),c=e.get("type"),p=e.get("format"),v=e.get("description"),m=n.get(t),y=e.get("default")||e.get("example")||"";""===y&&"object"===c&&(y=(0,i.getSampleSchema)(e,!1,{includeWriteOnly:!0})),"string"!=typeof y&&"object"===c&&(y=(0,i.stringify)(y));var b="string"===c&&("binary"===p||"base64"===p);return r.default.createElement("tr",{key:t,className:"parameters","data-property-name":t},r.default.createElement("td",{className:"col parameters-col_name"},r.default.createElement("div",{className:s?"parameter__name required":"parameter__name"},t,s?r.default.createElement("span",{style:{color:"red"}}," *"):null),r.default.createElement("div",{className:"parameter__type"},c,p&&r.default.createElement("span",{className:"prop-format"},"($",p,")"),g&&u.size?u.map(function(e,t){return r.default.createElement(C,{key:t+"-"+e,xKey:t,xVal:e})}):null),r.default.createElement("div",{className:"parameter__deprecated"},e.get("deprecated")?"deprecated":null)),r.default.createElement("td",{className:"col parameters-col_description"},r.default.createElement(h,{source:v}),f?r.default.createElement("div",null,r.default.createElement(S,{fn:l,dispatchInitialValue:!b,schema:e,description:t,getComponent:a,value:void 0===m?y:m,onChange:function(e){d(e,[t])}})):null))}))))}return r.default.createElement("div",null,y&&r.default.createElement(h,{source:y}),r.default.createElement(v,{getComponent:a,getConfigs:u,specSelectors:s,expandDepth:1,isExecute:f,schema:_.get("schema"),specPath:p.push("content",c),example:r.default.createElement(m,{requestBody:t,onChange:d,mediaType:c,getComponent:a,isExecute:f,specSelectors:s})}))}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=f(n(41)),o=f(n(4)),i=f(n(2)),a=f(n(3)),u=f(n(5)),s=f(n(6)),l=n(0),c=f(l);f(n(1)),f(n(12));function f(e){return e&&e.__esModule?e:{default:e}}var p=function(e){function t(){return(0,i.default)(this,t),(0,u.default)(this,(t.__proto__||(0,o.default)(t)).apply(this,arguments))}return(0,s.default)(t,e),(0,a.default)(t,[{key:"render",value:function(){var e=this.props,t=e.link,n=e.name,o=(0,e.getComponent)("Markdown"),i=t.get("operationId")||t.get("operationRef"),a=t.get("parameters")&&t.get("parameters").toJS(),u=t.get("description");return c.default.createElement("div",{style:{marginBottom:"1.5em"}},c.default.createElement("div",{style:{marginBottom:".5em"}},c.default.createElement("b",null,c.default.createElement("code",null,n)),u?c.default.createElement(o,{source:u}):null),c.default.createElement("pre",null,"Operation `",i,"`",c.default.createElement("br",null),c.default.createElement("br",null),"Parameters ",function(e,t){if("string"!=typeof t)return"";return t.split("\n").map(function(t,n){return n>0?Array(e+1).join(" ")+t:t}).join("\n")}(0,(0,r.default)(a,null,2))||"{}",c.default.createElement("br",null)))}}]),t}(l.Component);t.default=p},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=c(n(4)),o=c(n(2)),i=c(n(3)),a=c(n(5)),u=c(n(6)),s=c(n(0)),l=n(7);c(n(1)),c(n(12));function c(e){return e&&e.__esModule?e:{default:e}}var f=function(e){function t(){var e,n,i,u;(0,o.default)(this,t);for(var s=arguments.length,l=Array(s),c=0;c=e.length?(this._t=void 0,o(1)):o(0,"keys"==t?n:"values"==t?e[n]:[n,e[n]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},function(e,t){e.exports=function(){}},function(e,t){e.exports=function(e,t){return{value:t,done:!!e}}},function(e,t,n){"use strict";var r=n(160),o=n(95),i=n(97),a={};n(50)(a,n(19)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:o(1,n)}),i(e,t+" Iterator")}},function(e,t,n){var r=n(40),o=n(36),i=n(96);e.exports=n(44)?Object.defineProperties:function(e,t){o(e);for(var n,a=i(t),u=a.length,s=0;u>s;)r.f(e,n=a[s++],t[n]);return e}},function(e,t,n){var r=n(71),o=n(115),i=n(455);e.exports=function(e){return function(t,n,a){var u,s=r(t),l=o(s.length),c=i(a,l);if(e&&n!=n){for(;l>c;)if((u=s[c++])!=u)return!0}else for(;l>c;c++)if((e||c in s)&&s[c]===n)return e||c||0;return!e&&-1}}},function(e,t,n){var r=n(161),o=Math.max,i=Math.min;e.exports=function(e,t){return(e=r(e))<0?o(e+t,0):i(e,t)}},function(e,t,n){var r=n(161),o=n(156);e.exports=function(e){return function(t,n){var i,a,u=String(o(t)),s=r(n),l=u.length;return s<0||s>=l?e?"":void 0:(i=u.charCodeAt(s))<55296||i>56319||s+1===l||(a=u.charCodeAt(s+1))<56320||a>57343?e?u.charAt(s):i:e?u.slice(s,s+2):a-56320+(i-55296<<10)+65536}}},function(e,t,n){var r=n(36),o=n(165);e.exports=n(15).getIterator=function(e){var t=o(e);if("function"!=typeof t)throw TypeError(e+" is not iterable!");return r(t.call(e))}},function(e,t,n){n(459),n(245),n(470),n(474),n(485),n(486),e.exports=n(61).Promise},function(e,t,n){"use strict";var r=n(167),o={};o[n(18)("toStringTag")]="z",o+""!="[object z]"&&n(73)(Object.prototype,"toString",function(){return"[object "+r(this)+"]"},!0)},function(e,t,n){e.exports=!n(100)&&!n(101)(function(){return 7!=Object.defineProperty(n(169)("div"),"a",{get:function(){return 7}}).a})},function(e,t,n){var r=n(74);e.exports=function(e,t){if(!r(e))return e;var n,o;if(t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;if("function"==typeof(n=e.valueOf)&&!r(o=n.call(e)))return o;if(!t&&"function"==typeof(n=e.toString)&&!r(o=n.call(e)))return o;throw TypeError("Can't convert object to primitive value")}},function(e,t,n){"use strict";var r=n(463),o=n(244),i=n(171),a={};n(59)(a,n(18)("iterator"),function(){return this}),e.exports=function(e,t,n){e.prototype=r(a,{next:o(1,n)}),i(e,t+" Iterator")}},function(e,t,n){var r=n(60),o=n(464),i=n(251),a=n(170)("IE_PROTO"),u=function(){},s=function(){var e,t=n(169)("iframe"),r=i.length;for(t.style.display="none",n(252).appendChild(t),t.src="javascript:",(e=t.contentWindow.document).open(),e.write(" + + + + diff --git a/ygot-modified-files/string_type.go b/ygot-modified-files/string_type.go new file mode 100644 index 0000000000..32b0d2ec20 --- /dev/null +++ b/ygot-modified-files/string_type.go @@ -0,0 +1,199 @@ +// Copyright 2017 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 ytypes + +import ( + "bytes" + "fmt" + "regexp" + "unicode/utf8" + + "github.com/openconfig/goyang/pkg/yang" +) + +var regexpCache map[string]*regexp.Regexp = make(map[string]*regexp.Regexp) + +// Refer to: https://tools.ietf.org/html/rfc6020#section-9.4. + +// validateString validates value, which must be a Go string type, against the +// given schema. +func validateString(schema *yang.Entry, value interface{}) error { + // Check that the schema itself is valid. + if err := validateStringSchema(schema); err != nil { + return err + } + + // Check that type of value is the type expected from the schema. + stringVal, ok := value.(string) + if !ok { + return fmt.Errorf("non string type %T with value %v for schema %s", value, value, schema.Name) + } + + // Check that the length is within the allowed range. + allowedRanges := schema.Type.Length + strLen := uint64(utf8.RuneCountInString(stringVal)) + if !lengthOk(allowedRanges, strLen) { + return fmt.Errorf("length %d is outside range %v for schema %s", strLen, allowedRanges, schema.Name) + } + + // Check that the value satisfies any regex patterns. + for _, p := range schema.Type.Pattern { + var r *regexp.Regexp + if val, ok := regexpCache[p]; ok { + r = val + } else { + var err error + r, err = regexp.Compile(fixYangRegexp(p)) + if err != nil { + return err + } + regexpCache[p] = r + } + + // fixYangRegexp adds ^(...)$ around the pattern - the result is + // equivalent to a full match of whole string. + if !r.MatchString(stringVal) { + return fmt.Errorf("%q does not match regular expression pattern %q for schema %s", stringVal, r, schema.Name) + } + } + + return nil +} + +// validateStringSlice validates value, which must be a Go string slice type, +// against the given schema. +func validateStringSlice(schema *yang.Entry, value interface{}) error { + // Check that the schema itself is valid. + if err := validateStringSchema(schema); err != nil { + return err + } + + // Check that type of value is the type expected from the schema. + slice, ok := value.([]string) + if !ok { + return fmt.Errorf("non []string type %T with value %v for schema %s", value, value, schema.Name) + } + + // Each slice element must be valid and unique. + tbl := make(map[string]bool, len(slice)) + for i, val := range slice { + if err := validateString(schema, val); err != nil { + return fmt.Errorf("invalid element at index %d: %v for schema %s", i, err, schema.Name) + } + if tbl[val] { + return fmt.Errorf("duplicate string: %q for schema %s", val, schema.Name) + } + tbl[val] = true + } + return nil +} + +// validateStringSchema validates the given string type schema. This is a sanity +// check validation rather than a comprehensive validation against the RFC. +// It is assumed that such a validation is done when the schema is parsed from +// source YANG. +func validateStringSchema(schema *yang.Entry) error { + if schema == nil { + return fmt.Errorf("string schema is nil") + } + if schema.Type == nil { + return fmt.Errorf("string schema %s Type is nil", schema.Name) + } + if schema.Type.Kind != yang.Ystring { + return fmt.Errorf("string schema %s has wrong type %v", schema.Name, schema.Type.Kind) + } + + if schema.IsSchemaValidated { + return nil + } + + var err error + + for _, p := range schema.Type.Pattern { + _, ok := regexpCache[p] + if (ok == false) { + var r *regexp.Regexp + if r, err = regexp.Compile(fixYangRegexp(p)); err != nil { + return fmt.Errorf("error generating regexp %s %v for schema %s", p, err, schema.Name) + } else { + regexpCache[p] = r + } + } + } + + if err = validateLengthSchema(schema); err == nil { + schema.IsSchemaValidated = true + } + + return err +} + +// fixYangRegexp takes a pattern regular expression from a YANG module and +// returns it into a format which can be used by the Go regular expression +// library. YANG uses a W3C standard that is defined to be implicitly anchored +// at the head or tail of the expression. See +// https://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#regexs for details. +func fixYangRegexp(pattern string) string { + var buf bytes.Buffer + var inEscape bool + var prevChar rune + addParens := false + + for i, ch := range pattern { + if i == 0 && ch != '^' { + buf.WriteRune('^') + // Add parens around entire expression to prevent logical + // subexpressions associating with leading/trailing ^ / $. + buf.WriteRune('(') + addParens = true + } + + switch ch { + case '$': + // Dollar signs need to be escaped unless they are at + // the end of the pattern, or are already escaped. + if !inEscape && i != len(pattern)-1 { + buf.WriteRune('\\') + } + case '^': + // Carets need to be escaped unless they are already + // escaped, indicating set negation ([^.*]) or at the + // start of the string. + if !inEscape && prevChar != '[' && i != 0 { + buf.WriteRune('\\') + } + } + + // If the previous character was an escape character, then we + // leave the escape, otherwise check whether this is an escape + // char and if so, then enter escape. + inEscape = !inEscape && ch == '\\' + + buf.WriteRune(ch) + + if i == len(pattern)-1 { + if addParens { + buf.WriteRune(')') + } + if ch != '$' { + buf.WriteRune('$') + } + } + + prevChar = ch + } + + return buf.String() +} diff --git a/ygot-modified-files/ygot.patch b/ygot-modified-files/ygot.patch new file mode 100644 index 0000000000..01aaa1a5fb --- /dev/null +++ b/ygot-modified-files/ygot.patch @@ -0,0 +1,752 @@ +diff -ruN ygot-dir-orig/ygot/util/debug.go ygot-dir/ygot/util/debug.go +--- ygot-dir-orig/ygot/util/debug.go 2019-10-24 12:30:06.378629000 -0700 ++++ ygot-dir/ygot/util/debug.go 2019-10-24 12:31:25.059277000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package util + + import ( +@@ -53,6 +56,14 @@ + fmt.Println(globalIndent + out) + } + ++func IsDebugLibraryEnabled () bool { ++ return debugLibrary ++} ++ ++func IsDebugSchemaEnabled () bool { ++ return debugSchema ++} ++ + // DbgSchema prints v if the package global variable debugSchema is set. + // v has the same format as Printf. + func DbgSchema(v ...interface{}) { +@@ -177,6 +188,9 @@ + + // YangTypeToDebugString returns a debug string representation of a YangType. + func YangTypeToDebugString(yt *yang.YangType) string { ++ if !debugLibrary { ++ return "" ++ } + out := fmt.Sprintf("(TypeKind: %s", yang.TypeKindToName[yt.Kind]) + if len(yt.Pattern) != 0 { + out += fmt.Sprintf(", Pattern: %s", strings.Join(yt.Pattern, " or ")) +diff -ruN ygot-dir-orig/ygot/util/reflect.go ygot-dir/ygot/util/reflect.go +--- ygot-dir-orig/ygot/util/reflect.go 2019-10-24 12:30:06.403914000 -0700 ++++ ygot-dir/ygot/util/reflect.go 2019-10-24 12:31:25.063424000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package util + + import ( +@@ -196,8 +199,10 @@ + + // InsertIntoMap inserts value with key into parent which must be a map. + func InsertIntoMap(parentMap interface{}, key interface{}, value interface{}) error { +- DbgPrint("InsertIntoMap into parent type %T with key %v(%T) value \n%s\n (%T)", +- parentMap, ValueStrDebug(key), key, pretty.Sprint(value), value) ++ if debugLibrary { ++ DbgPrint("InsertIntoMap into parent type %T with key %v(%T) value \n%s\n (%T)", ++ parentMap, ValueStrDebug(key), key, pretty.Sprint(value), value) ++ } + + v := reflect.ValueOf(parentMap) + t := reflect.TypeOf(parentMap) +@@ -288,7 +293,7 @@ + n = reflect.Zero(ft.Type) + } + +- if !isFieldTypeCompatible(ft, n) { ++ if !isFieldTypeCompatible(ft, n) && !IsValueTypeCompatible(ft.Type, v) { + return fmt.Errorf("cannot assign value %v (type %T) to struct field %s (type %v) in struct %T", fieldValue, fieldValue, fieldName, ft.Type, parentStruct) + } + +diff -ruN ygot-dir-orig/ygot/util/schema.go ygot-dir/ygot/util/schema.go +--- ygot-dir-orig/ygot/util/schema.go 2019-10-24 12:30:06.417942000 -0700 ++++ ygot-dir/ygot/util/schema.go 2019-10-24 12:31:25.069042000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package util + + import ( +@@ -22,6 +25,8 @@ + "github.com/openconfig/goyang/pkg/yang" + ) + ++var schemaPathCache map[reflect.StructTag][][]string = make(map[reflect.StructTag][][]string) ++ + // IsLeafRef reports whether schema is a leafref schema node type. + func IsLeafRef(schema *yang.Entry) bool { + if schema == nil || schema.Type == nil { +@@ -68,17 +73,22 @@ + + // SchemaPaths returns all the paths in the path tag. + func SchemaPaths(f reflect.StructField) ([][]string, error) { +- var out [][]string +- pathTag, ok := f.Tag.Lookup("path") +- if !ok || pathTag == "" { +- return nil, fmt.Errorf("field %s did not specify a path", f.Name) +- } +- +- ps := strings.Split(pathTag, "|") +- for _, p := range ps { +- out = append(out, StripModulePrefixes(strings.Split(p, "/"))) ++ if tmpOut, ok := schemaPathCache[f.Tag]; ok { ++ return tmpOut, nil ++ } else { ++ var out [][]string ++ pathTag, ok := f.Tag.Lookup("path") ++ if !ok || pathTag == "" { ++ return nil, fmt.Errorf("field %s did not specify a path", f.Name) ++ } ++ ++ ps := strings.Split(pathTag, "|") ++ for _, p := range ps { ++ out = append(out, StripModulePrefixes(strings.Split(p, "/"))) ++ } ++ schemaPathCache[f.Tag] = out ++ return out, nil + } +- return out, nil + } + + // ChildSchema returns the first child schema that matches path from the given +@@ -233,7 +243,9 @@ + found := true + DbgSchema("traversing schema Dirs...") + for ; len(p) > 0; p = p[1:] { +- DbgSchema("/%s", p[0]) ++ if IsDebugSchemaEnabled() { ++ DbgSchema("/%s", p[0]) ++ } + var ok bool + s, ok = s.Dir[p[0]] + if !ok { +@@ -261,10 +273,13 @@ + return nil, nil + } + entries := FindFirstNonChoiceOrCase(schema) +- +- DbgSchema("checking for %s against non choice/case entries: %v\n", p[0], stringMapKeys(entries)) ++ if IsDebugSchemaEnabled() { ++ DbgSchema("checking for %s against non choice/case entries: %v\n", p[0], stringMapKeys(entries)) ++ } + for pe, entry := range entries { +- DbgSchema("%s ? ", pe) ++ if IsDebugSchemaEnabled() { ++ DbgSchema("%s ? ", pe) ++ } + if pe == p[0] { + DbgSchema(" - match\n") + return entry, nil +diff -ruN ygot-dir-orig/ygot/ytypes/container.go ygot-dir/ygot/ytypes/container.go +--- ygot-dir-orig/ygot/ytypes/container.go 2019-10-24 12:30:07.700737000 -0700 ++++ ygot-dir/ygot/ytypes/container.go 2019-10-24 12:31:26.682226000 -0700 +@@ -12,12 +12,15 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( + "fmt" + "reflect" +- ++ + "github.com/kylelemons/godebug/pretty" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/util" +@@ -71,7 +74,7 @@ + if errs := Validate(cschema, fieldValue); errs != nil { + errors = util.AppendErrs(errors, util.PrefixErrors(errs, cschema.Path())) + } +- case !util.IsValueNilOrDefault(structElems.Field(i).Interface()): ++ case !structElems.Field(i).IsNil(): + // Either an element in choice schema subtree, or bad field. + // If the former, it will be found in the choice check below. + extraFields[fieldName] = nil +@@ -217,7 +220,10 @@ + } + } + +- util.DbgPrint("container after unmarshal:\n%s\n", pretty.Sprint(destv.Interface())) ++ if util.IsDebugLibraryEnabled() { ++ util.DbgPrint("container after unmarshal:\n%s\n", pretty.Sprint(destv.Interface())) ++ } ++ + return nil + } + +diff -ruN ygot-dir-orig/ygot/ytypes/leaf.go ygot-dir/ygot/ytypes/leaf.go +--- ygot-dir-orig/ygot/ytypes/leaf.go 2019-10-24 12:30:07.705496000 -0700 ++++ ygot-dir/ygot/ytypes/leaf.go 2019-10-24 12:31:26.691433000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +@@ -79,7 +82,7 @@ + + switch ykind { + case yang.Ybinary: +- return util.NewErrs(validateBinary(schema, value)) ++ return util.NewErrs(validateBinary(schema, rv)) + case yang.Ybits: + return nil + // TODO(mostrowski): restore when representation is decided. +@@ -252,7 +255,7 @@ + // 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 +286,25 @@ + // 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)) +@@ -418,12 +429,10 @@ + return nil + } + +-// YANGEmpty is a derived type which is used to represent the YANG empty type. ++// YANGEmpty is a derived type which is used to represent the YANG ++// empty type. + type YANGEmpty bool + +-// Binary is a derived type which is used to represent the YANG binary type. +-type Binary []byte +- + // unmarshalLeaf unmarshals a scalar value (determined by json.Unmarshal) into + // the parent containing the leaf. + // schema points to the schema for the leaf type. +@@ -720,7 +729,9 @@ + return nil, fmt.Errorf("%s ΛEnumTypes function returned wrong type %T, want map[string][]reflect.Type", t, ei) + } + +- util.DbgPrint("path is %s for schema %s", absoluteSchemaDataPath(schema), schema.Name) ++ if util.IsDebugLibraryEnabled() { ++ util.DbgPrint("path is %s for schema %s", absoluteSchemaDataPath(schema), schema.Name) ++ } + + return enumTypesMap[absoluteSchemaDataPath(schema)], nil + } +diff -ruN ygot-dir-orig/ygot/ytypes/list.go ygot-dir/ygot/ytypes/list.go +--- ygot-dir-orig/ygot/ytypes/list.go 2019-10-24 12:30:07.712731000 -0700 ++++ ygot-dir/ygot/ytypes/list.go 2019-10-24 12:31:26.696852000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +@@ -217,6 +220,9 @@ + if len(schema.Key) == 0 { + return fmt.Errorf("list %s with config set must have a key", schema.Name) + } ++ if schema.IsSchemaValidated == true { ++ return nil ++ } + keys := strings.Split(schema.Key, " ") + keysMissing := make(map[string]bool) + for _, v := range keys { +@@ -232,6 +238,7 @@ + } + } + ++ schema.IsSchemaValidated = true + return nil + } + +@@ -282,10 +289,10 @@ + if util.IsValueNil(jsonList) { + return nil + } +- // Check that the schema itself is valid. ++ + if err := validateListSchema(schema); err != nil { + return err +- } ++ } + + util.DbgPrint("unmarshalList jsonList %v, type %T, into parent type %T, schema name %s", util.ValueStrDebug(jsonList), jsonList, parent, schema.Name) + +@@ -350,7 +357,9 @@ + return err + } + } +- util.DbgPrint("list after unmarshal:\n%s\n", pretty.Sprint(parent)) ++ if util.IsDebugLibraryEnabled() { ++ util.DbgPrint("list after unmarshal:\n%s\n", pretty.Sprint(parent)) ++ } + + return nil + } +@@ -388,17 +397,96 @@ + if err != nil { + return err + } +- + fv := val.Elem().FieldByName(fn) + ft := fv.Type() + if util.IsValuePtr(fv) { + ft = ft.Elem() + } +- +- nv, err := StringToType(ft, fieldVal) ++ sf, ok := val.Elem().Type().FieldByName(fn) ++ if ok == false { ++ return fmt.Errorf("Field %s not present in the struct %s", fn, val.Elem()) ++ } ++ cschema, err := childSchema(schema, sf) + if err != nil { + return err + } ++ keyLeafKind := cschema.Type.Kind ++ if keyLeafKind == yang.Yleafref { ++ lrfschema, err := resolveLeafRef(cschema) ++ if err != nil { ++ return err ++ } ++ keyLeafKind = lrfschema.Type.Kind ++ } ++ ++ var nv reflect.Value ++ if keyLeafKind == yang.Yunion && strings.HasSuffix(keyT.Name(), "_Union") { ++ sks, err := getUnionKindsNotEnums(cschema) ++ if err != nil { ++ return err ++ } ++ for _, sk := range sks { ++ gv, err := StringToType(reflect.TypeOf(yangBuiltinTypeToGoType(sk)), fieldVal) ++ if err == nil { ++ mn := "To_" + ft.Name() ++ mapMethod := val.MethodByName(mn) ++ if !mapMethod.IsValid() { ++ return fmt.Errorf("%s does not have a %s function", val, mn) ++ } ++ ec := mapMethod.Call([]reflect.Value{gv}) ++ if len(ec) != 2 { ++ return fmt.Errorf("%s %s function returns %d params", ft.Name(), mn, len(ec)) ++ } ++ ei := ec[0].Interface() ++ ee := ec[1].Interface() ++ if ee != nil { ++ return fmt.Errorf("unmarshaled %v type %T does not have a union type: %v", fieldVal, fieldVal, ee) ++ } ++ nv = reflect.ValueOf(ei) ++ break ++ } ++ } ++ ++ if nv.IsValid() == false { ++ ets, err := schemaToEnumTypes(cschema, elmT) ++ if err != nil { ++ return err ++ } ++ for _, et := range ets { ++ ev, err := castToEnumValue(et, fieldVal) ++ if err != nil { ++ return err ++ } ++ if ev != nil { ++ mn := "To_" + ft.Name() ++ mapMethod := val.MethodByName(mn) ++ if !mapMethod.IsValid() { ++ return fmt.Errorf("%s does not have a %s function", val, mn) ++ } ++ ec := mapMethod.Call([]reflect.Value{reflect.ValueOf(ev)}) ++ if len(ec) != 2 { ++ return fmt.Errorf("%s %s function returns %d params", ft.Name(), mn, len(ec)) ++ } ++ ei := ec[0].Interface() ++ ee := ec[1].Interface() ++ if ee != nil { ++ return fmt.Errorf("unmarshaled %v type %T does not have a union type: %v", fieldVal, fieldVal, ee) ++ } ++ nv = reflect.ValueOf(ei) ++ break ++ } ++ fmt.Errorf("could not unmarshal %v into enum type: %s\n", fieldVal, err) ++ } ++ if nv.IsValid() == false { ++ return fmt.Errorf("could not create the value type for the field name %s with the value %s", fn, fieldVal) ++ } ++ } ++ } else { ++ nv, err = StringToType(ft, fieldVal) ++ if err != nil { ++ return err ++ } ++ } + return util.InsertIntoStruct(val.Interface(), fn, nv.Interface()) + } + +@@ -494,6 +582,9 @@ + } + + // TODO(yusufsn): When the key is a leafref, its target should be filled out. ++ if (len(keys) == 0) { ++ return nil, nil ++ } + mapVal, err := makeValForInsert(schema, root, keys) + if err != nil { + return nil, fmt.Errorf("failed to create map value for insert, root %T, keys %v: %v", root, keys, err) +diff -ruN ygot-dir-orig/ygot/ytypes/node.go ygot-dir/ygot/ytypes/node.go +--- ygot-dir-orig/ygot/ytypes/node.go 2019-10-24 12:30:07.727365000 -0700 ++++ ygot-dir/ygot/ytypes/node.go 2019-10-24 12:31:26.701328000 -0700 +@@ -12,17 +12,19 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +- "reflect" +- + "github.com/golang/protobuf/proto" + "github.com/openconfig/goyang/pkg/yang" + "github.com/openconfig/ygot/util" + "github.com/openconfig/ygot/ygot" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ++ "reflect" + + gpb "github.com/openconfig/gnmi/proto/gnmi" + ) +@@ -129,6 +131,16 @@ + if err := util.InitializeStructField(root, ft.Name); err != nil { + return nil, status.Errorf(codes.Unknown, "failed to initialize struct field %s in %T, child schema %v, path %v", ft.Name, root, cschema, path) + } ++ ++ if cschema.IsLeaf() || cschema.IsLeafList() { ++ if len(path.Elem) == 1 && len(path.Elem[0].Key) == 1 { ++ var vals []string ++ vals = append(vals, path.Elem[0].Key[path.Elem[0].Name]) ++ if args.val, err = ygot.EncodeTypedValue(vals, gpb.Encoding_JSON_IETF); err != nil { ++ return nil, status.Errorf(codes.Unknown, "failed to get the typed value '%v' for leaf/leaf-list => %s in %T ; because of %v", vals, ft.Name, root, err) ++ } ++ } ++ } + } + + // If val in args is set to a non-nil value and the path is exhausted, we +@@ -286,6 +298,11 @@ + if err != nil { + return nil, err + } ++ ++ if (key == nil) { ++ return []*TreeNode{{Path: traversedPath,Schema: schema,Data: root,}}, nil ++ } ++ + nodes, err := retrieveNode(schema, rv.MapIndex(reflect.ValueOf(key)).Interface(), util.PopGNMIPath(path), appendElem(traversedPath, path.GetElem()[0]), args) + if err != nil { + return nil, err +diff -ruN ygot-dir-orig/ygot/ytypes/string_type.go ygot-dir/ygot/ytypes/string_type.go +--- ygot-dir-orig/ygot/ytypes/string_type.go 2019-10-24 12:30:07.734288000 -0700 ++++ ygot-dir/ygot/ytypes/string_type.go 2019-10-24 12:31:26.705649000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +@@ -23,6 +26,8 @@ + "github.com/openconfig/goyang/pkg/yang" + ) + ++var regexpCache map[string]*regexp.Regexp = make(map[string]*regexp.Regexp) ++ + // Refer to: https://tools.ietf.org/html/rfc6020#section-9.4. + + // validateString validates value, which must be a Go string type, against the +@@ -48,10 +53,18 @@ + + // Check that the value satisfies any regex patterns. + for _, p := range schema.Type.Pattern { +- r, err := regexp.Compile(fixYangRegexp(p)) +- if err != nil { +- return err ++ var r *regexp.Regexp ++ if val, ok := regexpCache[p]; ok { ++ r = val ++ } else { ++ var err error ++ r, err = regexp.Compile(fixYangRegexp(p)) ++ if err != nil { ++ return err ++ } ++ regexpCache[p] = r + } ++ + // fixYangRegexp adds ^(...)$ around the pattern - the result is + // equivalent to a full match of whole string. + if !r.MatchString(stringVal) { +@@ -105,13 +118,29 @@ + return fmt.Errorf("string schema %s has wrong type %v", schema.Name, schema.Type.Kind) + } + ++ if schema.IsSchemaValidated { ++ return nil ++ } ++ ++ var err error ++ + for _, p := range schema.Type.Pattern { +- if _, err := regexp.Compile(fixYangRegexp(p)); err != nil { +- return fmt.Errorf("error generating regexp %s %v for schema %s", p, err, schema.Name) +- } ++ _, ok := regexpCache[p] ++ if (ok == false) { ++ var r *regexp.Regexp ++ if r, err = regexp.Compile(fixYangRegexp(p)); err != nil { ++ return fmt.Errorf("error generating regexp %s %v for schema %s", p, err, schema.Name) ++ } else { ++ regexpCache[p] = r ++ } ++ } + } + +- return validateLengthSchema(schema) ++ if err = validateLengthSchema(schema); err == nil { ++ schema.IsSchemaValidated = true ++ } ++ ++ return err + } + + // fixYangRegexp takes a pattern regular expression from a YANG module and +diff -ruN ygot-dir-orig/ygot/ytypes/unmarshal.go ygot-dir/ygot/ytypes/unmarshal.go +--- ygot-dir-orig/ygot/ytypes/unmarshal.go 2019-10-24 12:30:07.753024000 -0700 ++++ ygot-dir/ygot/ytypes/unmarshal.go 2019-10-24 12:31:26.710027000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +@@ -73,7 +76,10 @@ + if schema == nil { + return fmt.Errorf("nil schema for parent type %T, value %v (%T)", parent, value, value) + } +- util.DbgPrint("Unmarshal value %v, type %T, into parent type %T, schema name %s", util.ValueStrDebug(value), value, parent, schema.Name) ++ ++ if (util.IsDebugLibraryEnabled()) { ++ util.DbgPrint("Unmarshal value %v, type %T, into parent type %T, schema name %s", util.ValueStrDebug(value), value, parent, schema.Name) ++ } + + if enc == GNMIEncoding && !(schema.IsLeaf() || schema.IsLeafList()) { + return errors.New("unmarshalling a non leaf node isn't supported in GNMIEncoding mode") +diff -ruN ygot-dir-orig/ygot/ytypes/util_schema.go ygot-dir/ygot/ytypes/util_schema.go +--- ygot-dir-orig/ygot/ytypes/util_schema.go 2019-10-24 12:30:07.763728000 -0700 ++++ ygot-dir/ygot/ytypes/util_schema.go 2019-10-24 12:31:26.715104000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +@@ -23,6 +26,8 @@ + "github.com/openconfig/ygot/util" + ) + ++var pathToSchemaCache map[reflect.StructTag][]string = make(map[reflect.StructTag][]string) ++ + // validateLengthSchema validates whether the given schema has a valid length + // specification. + func validateLengthSchema(schema *yang.Entry) error { +@@ -137,8 +142,16 @@ + // if the struct tag is invalid, or nil if tag is valid but the schema is not + // found in the tree at the specified path. + func childSchema(schema *yang.Entry, f reflect.StructField) (*yang.Entry, error) { +- pathTag, _ := f.Tag.Lookup("path") +- util.DbgSchema("childSchema for schema %s, field %s, tag %s\n", schema.Name, f.Name, pathTag) ++ if (schema.ChildSchemaCache == nil) { ++ schema.ChildSchemaCache = make(map[reflect.StructTag]*yang.Entry) ++ } else if cschema, ok := schema.ChildSchemaCache[f.Tag]; ok { ++ return cschema, nil ++ } ++ ++ if util.IsDebugSchemaEnabled() { ++ pathTag, _ := f.Tag.Lookup("path") ++ util.DbgSchema("childSchema for schema %s, field %s, tag %s\n", schema.Name, f.Name, pathTag) ++ } + p, err := pathToSchema(f) + if err != nil { + return nil, err +@@ -168,6 +181,7 @@ + } + if foundSchema { + util.DbgSchema(" - found\n") ++ schema.ChildSchemaCache[f.Tag] = childSchema + return childSchema, nil + } + util.DbgSchema(" - not found\n") +@@ -183,21 +197,25 @@ + // path element i.e. choice1/case1/leaf1 path in the schema will have + // struct tag `path:"leaf1"`. This implies that only paths with length + // 1 are eligible for this matching. ++ schema.ChildSchemaCache[f.Tag] = nil + return nil, nil + } + entries := util.FindFirstNonChoiceOrCase(schema) +- +- util.DbgSchema("checking for %s against non choice/case entries: %v\n", p[0], stringMapKeys(entries)) ++ if util.IsDebugSchemaEnabled() { ++ util.DbgSchema("checking for %s against non choice/case entries: %v\n", p[0], stringMapKeys(entries)) ++ } + for name, entry := range entries { + util.DbgSchema("%s ? ", name) + + if util.StripModulePrefix(name) == p[0] { + util.DbgSchema(" - match\n") ++ schema.ChildSchemaCache[f.Tag] = entry + return entry, nil + } + } + + util.DbgSchema(" - no matches\n") ++ schema.ChildSchemaCache[f.Tag] = nil + return nil, nil + } + +@@ -239,25 +257,32 @@ + // leafref. In the latter case, this function returns {"config", "a"}, and the + // schema *yang.Entry for the field is given by schema.Dir["config"].Dir["a"]. + func pathToSchema(f reflect.StructField) ([]string, error) { +- pathAnnotation, ok := f.Tag.Lookup("path") +- if !ok { +- return nil, fmt.Errorf("field %s did not specify a path", f.Name) +- } +- +- paths := strings.Split(pathAnnotation, "|") +- if len(paths) == 1 { +- pathAnnotation = strings.TrimPrefix(pathAnnotation, "/") +- return strings.Split(pathAnnotation, "/"), nil +- } +- for _, pv := range paths { +- pv = strings.TrimPrefix(pv, "/") +- pe := strings.Split(pv, "/") +- if len(pe) > 1 { ++ if pe, ok := pathToSchemaCache[f.Tag]; ok { ++ return pe, nil ++ } else { ++ pathAnnotation, ok := f.Tag.Lookup("path") ++ if !ok { ++ return nil, fmt.Errorf("field %s did not specify a path", f.Name) ++ } ++ ++ paths := strings.Split(pathAnnotation, "|") ++ if len(paths) == 1 { ++ pathAnnotation = strings.TrimPrefix(pathAnnotation, "/") ++ pe := strings.Split(pathAnnotation, "/") ++ pathToSchemaCache[f.Tag] = pe + return pe, nil + } ++ for _, pv := range paths { ++ pv = strings.TrimPrefix(pv, "/") ++ pe := strings.Split(pv, "/") ++ if len(pe) > 1 { ++ pathToSchemaCache[f.Tag] = pe ++ return pe, nil ++ } ++ } ++ ++ return nil, fmt.Errorf("field %s had path tag %s with |, but no elements of form a/b", f.Name, pathAnnotation) + } +- +- return nil, fmt.Errorf("field %s had path tag %s with |, but no elements of form a/b", f.Name, pathAnnotation) + } + + // directDescendantSchema returns the direct descendant schema for the struct +diff -ruN ygot-dir-orig/ygot/ytypes/validate.go ygot-dir/ygot/ytypes/validate.go +--- ygot-dir-orig/ygot/ytypes/validate.go 2019-10-24 12:30:07.778829000 -0700 ++++ ygot-dir/ygot/ytypes/validate.go 2019-10-24 12:31:26.719650000 -0700 +@@ -12,6 +12,9 @@ + // See the License for the specific language governing permissions and + // limitations under the License. + ++// This file is changed by Broadcom. ++// Modifications - Copyright 2019 Broadcom. The term Broadcom refers to Broadcom Inc. and/or its subsidiaries. ++ + package ytypes + + import ( +@@ -74,7 +77,7 @@ + errs = ValidateLeafRefData(schema, value, leafrefOpt) + } + +- util.DbgPrint("Validate with value %v, type %T, schema name %s", util.ValueStr(value), value, schema.Name) ++ util.DbgPrint("Validate with value %v, type %T, schema name %s", util.ValueStrDebug(value), value, schema.Name) + + switch { + case schema.IsLeaf():