From 1b033f0f6e9c1cc922a78072ec2fc22e0356b20c Mon Sep 17 00:00:00 2001 From: Praveen Chaudhary Date: Tue, 10 Dec 2019 22:01:52 -0800 Subject: [PATCH] =?UTF-8?q?[=5Fsonic=5Fyang=5Fext.py]:=20Extend=20sonic=20?= =?UTF-8?q?yang=20class=20to=20support=20cropping,=20transalation,=20rever?= =?UTF-8?q?se=20translation=20of=20=E2=80=A6=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [_sonic_yang_ext.py]: Extend sonic yang class to support cropping of config. From the json format of yang models, a map is created from config DB tables to container in yang model. Input Config is cropped on based of this map. * [test_sonic_yang.py]: Test code for sonic yang extension funtionalities. Test code for copping config DB. Minor fixes in other files. * [_sonic_yang_ext.py]: Translate Config DB format to YANG json as per yang model. Load data in sonic_yang after transalation. Test for translation functionality. * [test_sonic_yang.py]: Add test for translation. Added config DB json sample in yangTest.json. Blocked other PLY test cases as of now, because they fail with new yang models. * [_sonic_yang_ext.py]: Reverse translation i.e. from YANG json to Config DB json. Reverse translation i.e. from YANG json to Config DB json based on yang models. Find xpath for a port. portleaf and a yang list. get_data functions to get rev xlated data from data tree. Test for crop, xlate and rev xlate. * [_sonic_yang_ext.py]: Minor changes to handle exceptions. * [build_debian.sh]: Add neccessary package in sonic image and in sonic slave docker. Fix the test to accomodate for ip-prefix as of now * [_sonic_yang_ext.py]: Addressing more exception handling and comments. Unblocking PLY test cases. * [setup.py]: Add _sonic_yang_ext.py in package. * [test_sonic_yang.py]: Fixing test case for delete node. --- build_debian.sh | 4 + sonic-slave-stretch/Dockerfile.j2 | 1 + src/sonic-yang-mgmt/_sonic_yang_ext.py | 628 +++++++++++++++++ src/sonic-yang-mgmt/setup.py | 4 +- src/sonic-yang-mgmt/sonic_yang.py | 67 +- .../sample_config_db.json | 648 +++++++++++++++++ .../libyang-python-tests/test_sonic_yang.py | 53 +- .../tests/yang-model-tests/yangTest.json | 649 ++++++++++++++++++ 8 files changed, 2029 insertions(+), 25 deletions(-) create mode 100644 src/sonic-yang-mgmt/_sonic_yang_ext.py create mode 100644 src/sonic-yang-mgmt/tests/libyang-python-tests/sample_config_db.json diff --git a/build_debian.sh b/build_debian.sh index fb3ecfef6ce6..4cbf379579c7 100755 --- a/build_debian.sh +++ b/build_debian.sh @@ -416,6 +416,10 @@ sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install 'docker sudo LANG=C DEBIAN_FRONTEND=noninteractive chroot $FILESYSTEM_ROOT apt-get -y install gcc libpython2.7-dev sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install 'netifaces==0.10.7' +# Get package to support Dynamic Port Breakout +sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install xmltodict +sudo https_proxy=$https_proxy LANG=C chroot $FILESYSTEM_ROOT pip install jsondiff + ## Create /var/run/redis folder for docker-database to mount sudo mkdir -p $FILESYSTEM_ROOT/var/run/redis diff --git a/sonic-slave-stretch/Dockerfile.j2 b/sonic-slave-stretch/Dockerfile.j2 index 66cf42545339..311c38ce5218 100644 --- a/sonic-slave-stretch/Dockerfile.j2 +++ b/sonic-slave-stretch/Dockerfile.j2 @@ -354,6 +354,7 @@ RUN pip install setuptools==40.8.0 # For sonic_yang_mgmt build RUN pip install ijson +RUN pip install jsondiff # Install dependencies for isc-dhcp-relay build RUN apt-get -y build-dep isc-dhcp diff --git a/src/sonic-yang-mgmt/_sonic_yang_ext.py b/src/sonic-yang-mgmt/_sonic_yang_ext.py new file mode 100644 index 000000000000..ddfc6010171c --- /dev/null +++ b/src/sonic-yang-mgmt/_sonic_yang_ext.py @@ -0,0 +1,628 @@ +# This script is used as extension of sonic_yang class. It has methods of +# class sonic_yang. A separate file is used to avoid a single large file. + +import yang as ly +import re +import pprint + +from json import dump, load, dumps, loads +from xmltodict import parse +from os import listdir, walk, path +from os.path import isfile, join, splitext +from glob import glob + +# class sonic_yang methods + +""" +load all YANG models, create JSON of yang models +""" +def loadYangModel(self): + + try: + yangDir = self.yang_dir + self.yangFiles = glob(yangDir +"/*.yang") + for file in self.yangFiles: + if (self.load_schema_module(file) == False): + return False + + # keep only modules name in self.yangFiles + self.yangFiles = [f.split('/')[-1] for f in self.yangFiles] + self.yangFiles = [f.split('.')[0] for f in self.yangFiles] + print('Loaded below Yang Models') + print(self.yangFiles) + + # load json for each yang model + self.loadJsonYangModel() + # create a map from config DB table to yang container + self.createDBTableToModuleMap() + + except Exception as e: + print("Yang Models Load failed") + raise e + + return True + +""" +load JSON schema format from yang models +""" +def loadJsonYangModel(self): + + try: + for f in self.yangFiles: + m = self.ctx.get_module(f) + if m is not None: + xml = m.print_mem(ly.LYD_JSON, ly.LYP_FORMAT) + self.yJson.append(parse(xml)) + except Exception as e: + print('JSON conversion for yang models failed') + raise e + + return + +""" +Create a map from config DB tables to container in yang model +This module name and topLevelContainer are fetched considering YANG models are +written using below Guidelines: +https://github.com/Azure/SONiC/blob/master/doc/mgmt/SONiC_YANG_Model_Guidelines.md. +""" +def createDBTableToModuleMap(self): + + for j in self.yJson: + # get module name + moduleName = j['module']['@name'] + if "sonic-head" in moduleName or "sonic-common" in moduleName: + continue; + # get all top level container + topLevelContainer = j['module']['container'] + if topLevelContainer is None: + raise Exception("topLevelContainer not found") + + assert topLevelContainer['@name'] == moduleName + + container = topLevelContainer['container'] + # container is a list + if isinstance(container, list): + for c in container: + self.confDbYangMap[c['@name']] = { + "module" : moduleName, + "topLevelContainer": topLevelContainer['@name'], + "container": c + } + # container is a dict + else: + self.confDbYangMap[container['@name']] = { + "module" : moduleName, + "topLevelContainer": topLevelContainer['@name'], + "container": container + } + return + +""" +Get module, topLevelContainer(TLC) and json container for a config DB table +""" +def get_module_TLC_container(self, table): + cmap = self.confDbYangMap + m = cmap[table]['module'] + t = cmap[table]['topLevelContainer'] + c = cmap[table]['container'] + return m, t, c + +""" +Crop config as per yang models, +This Function crops from config only those TABLEs, for which yang models is +provided. +""" +def cropConfigDB(self, croppedFile=None): + + for table in self.jIn.keys(): + if table not in self.confDbYangMap: + del self.jIn[table] + + if croppedFile: + with open(croppedFile, 'w') as f: + dump(self.jIn, f, indent=4) + + return + +""" +Extract keys from table entry in Config DB and return in a dict +For Example: regex = | and tableKey = "Vlan111|2a04:5555:45:6709::1/64" + +1.) first code will extract key list from regex, i.e. vlan_name and ip_prefix. +2.) then will create another regex(regexV) to extract Values from tableKey by + replacing " --> extractor i.e. (.*?)" in regex. +3.) Then will extract values from tableKey with regexV. +4.) Resulting Dict will be: +KeyDict = {"vlan_name": "Vlan111", "ip-prefix": "2a04:5555:45:6709::1/64"} +""" +def extractKey(self, tableKey, regex): + + # get the keys from regex of key extractor + keyList = re.findall(r'<(.*?)>', regex) + # create a regex to get values from tableKey + # and change separator to text in regexV + regexV = re.sub('<.*?>', '(.*?)', regex) + regexV = re.sub('\|', '\\|', regexV) + # get the value groups + value = re.match(r'^'+regexV+'$', tableKey) + # create the keyDict + i = 1 + keyDict = dict() + for k in keyList: + if value.group(i): + keyDict[k] = value.group(i) + else: + raise Exception("Value not found for {} in {}".format(k, tableKey)) + i = i + 1 + + return keyDict + +""" +Fill the dict based on leaf as a list or dict @model yang model object +""" +def fillLeafDict(self, leafs, leafDict, isleafList=False): + + if leafs == None: + return + + # fill default values + def fillSteps(leaf): + leaf['__isleafList'] = isleafList + leafDict[leaf['@name']] = leaf + return + + if isinstance(leafs, list): + for leaf in leafs: + #print("{}:{}".format(leaf['@name'], leaf)) + fillSteps(leaf) + else: + #print("{}:{}".format(leaf['@name'], leaf)) + fillSteps(leafs) + + return + +""" +create a dict to map each key under primary key with a dict yang model. +This is done to improve performance of mapping from values of TABLEs in +config DB to leaf in YANG LIST. +""" +def createLeafDict(self, model): + + leafDict = dict() + #Iterate over leaf, choices and leaf-list. + self.fillLeafDict(model.get('leaf'), leafDict) + + #choices, this is tricky, since leafs are under cases in tree. + choices = model.get('choice') + if choices: + for choice in choices: + cases = choice['case'] + for case in cases: + self.fillLeafDict(case.get('leaf'), leafDict) + + # leaf-lists + self.fillLeafDict(model.get('leaf-list'), leafDict, True) + + return leafDict + +""" +Convert a string from Config DB value to Yang Value based on type of the +key in Yang model. +@model : A List of Leafs in Yang model list +""" +def findYangTypedValue(self, key, value, leafDict): + + # convert config DB string to yang Type + def yangConvert(val): + # Convert everything to string + val = str(val) + # find type of this key from yang leaf + type = leafDict[key]['type']['@name'] + + if 'uint' in type: + vValue = int(val, 10) + # TODO: find type of leafref from schema node + elif 'leafref' in type: + vValue = val + #TODO: find type in sonic-head, as of now, all are enumeration + elif 'head:' in type: + vValue = val + else: + vValue = val + return vValue + + # if it is a leaf-list do it for each element + if leafDict[key]['__isleafList']: + vValue = list() + for v in value: + vValue.append(yangConvert(v)) + else: + vValue = yangConvert(value) + + return vValue + +""" +Xlate a list +This function will xlate from a dict in config DB to a Yang JSON list +using yang model. Output will be go in self.xlateJson +""" +def xlateList(self, model, yang, config, table): + + # TODO: define a keyExt dict as of now, but we should be able to extract + # this from YANG model extentions. + keyExt = { + "VLAN_INTERFACE": "|", + "ACL_RULE": "|", + "VLAN": "", + "VLAN_MEMBER": "|", + "ACL_TABLE": "", + "INTERFACE": "|", + "PORT": "" + } + #create a dict to map each key under primary key with a dict yang model. + #This is done to improve performance of mapping from values of TABLEs in + #config DB to leaf in YANG LIST. + + leafDict = self.createLeafDict(model) + + # Find and extracts key from each dict in config + for pkey in config: + try: + keyDict = self.extractKey(pkey, keyExt[table]) + # fill rest of the values in keyDict + for vKey in config[pkey]: + keyDict[vKey] = self.findYangTypedValue(vKey, \ + config[pkey][vKey], leafDict) + yang.append(keyDict) + except Exception as e: + print("Exception while Config DB --> YANG: pkey:{}, "\ + "vKey:{}, value: {}".format(pkey, vKey, config[pkey][vKey])) + raise e + + return + +""" +Xlate a container +This function will xlate from a dict in config DB to a Yang JSON container +using yang model. Output will be stored in self.xlateJson +""" +def xlateContainer(self, model, yang, config, table): + + # if container contains single list with containerName_LIST and + # config is not empty then xLate the list + clist = model.get('list') + if clist and isinstance(clist, dict) and \ + clist['@name'] == model['@name']+"_LIST" and bool(config): + #print(clist['@name']) + yang[clist['@name']] = list() + self.xlateList(model['list'], yang[clist['@name']], \ + config, table) + #print(yang[clist['@name']]) + + # TODO: Handle mupltiple list and rest of the field in Container. + # We do not have any such instance in Yang model today. + + return + +""" +xlate ConfigDB json to Yang json +""" +def xlateConfigDBtoYang(self, jIn, yangJ): + + # find top level container for each table, and run the xlate_container. + for table in jIn.keys(): + cmap = self.confDbYangMap[table] + # create top level containers + key = cmap['module']+":"+cmap['topLevelContainer'] + subkey = cmap['topLevelContainer']+":"+cmap['container']['@name'] + # Add new top level container for first table in this container + yangJ[key] = dict() if yangJ.get(key) is None else yangJ[key] + yangJ[key][subkey] = dict() + self.xlateContainer(cmap['container'], yangJ[key][subkey], \ + jIn[table], table) + + return + +""" +Read config file and crop it as per yang models +""" +def xlateConfigDB(self, xlateFile=None): + + jIn= self.jIn + yangJ = self.xlateJson + # xlation is written in self.xlateJson + self.xlateConfigDBtoYang(jIn, yangJ) + + if xlateFile: + with open(xlateFile, 'w') as f: + dump(self.xlateJson, f, indent=4) + + return + +""" +create config DB table key from entry in yang JSON +""" +def createKey(self, entry, regex): + + keyDict = dict() + keyV = regex + # get the keys from regex of key extractor + keyList = re.findall(r'<(.*?)>', regex) + for key in keyList: + val = entry.get(key) + if val: + #print("pair: {} {}".format(key, val)) + keyDict[key] = sval = str(val) + keyV = re.sub(r'<'+key+'>', sval, keyV) + #print("VAL: {} {}".format(regex, keyV)) + else: + raise Exception("key {} not found in entry".format(key)) + #print("kDict {}".format(keyDict)) + return keyV, keyDict + +""" +Convert a string from Config DB value to Yang Value based on type of the +key in Yang model. +@model : A List of Leafs in Yang model list +""" +def revFindYangTypedValue(self, key, value, leafDict): + + # convert yang Type to config DB string + def revYangConvert(val): + # config DB has only strings, thank god for that :), wait not yet!!! + return str(val) + + # if it is a leaf-list do it for each element + if leafDict[key]['__isleafList']: + vValue = list() + for v in value: + vValue.append(revYangConvert(v)) + else: + vValue = revYangConvert(value) + + return vValue + + +""" +Rev xlate from _LIST to table in config DB +""" +def revXlateList(self, model, yang, config, table): + + # TODO: define a keyExt dict as of now, but we should be able to + # extract this from YANG model extentions. + keyExt = { + "VLAN_INTERFACE": "|", + "ACL_RULE": "|", + "VLAN": "", + "VLAN_MEMBER": "|", + "ACL_TABLE": "", + "INTERFACE": "|", + "PORT": "" + } + + # create a dict to map each key under primary key with a dict yang model. + # This is done to improve performance of mapping from values of TABLEs in + # config DB to leaf in YANG LIST. + leafDict = self.createLeafDict(model) + + # list with name
_LIST should be removed, + # right now we have only this instance of LIST + if model['@name'] == table + "_LIST": + for entry in yang: + # create key of config DB table + pkey, pkeydict = self.createKey(entry, keyExt[table]) + config[pkey]= dict() + # fill rest of the entries + for key in entry: + if key not in pkeydict: + config[pkey][key] = self.revFindYangTypedValue(key, \ + entry[key], leafDict) + + return + +""" +Rev xlate from yang container to table in config DB +""" +def revXlateContainer(self, model, yang, config, table): + + # Note: right now containers has only LISTs. + # IF container has only one list + if isinstance(model['list'], dict): + modelList = model['list'] + # Pass matching list from Yang Json + self.revXlateList(modelList, yang[modelList['@name']], config, table) + else: + # TODO: Container[TABLE] contains multiple lists. [Test Pending] + # No instance now. + for modelList in model['list']: + self.revXlateList(modelList, yang[modelList['@name']], config, table) + + return + +""" +rev xlate ConfigDB json to Yang json +""" +def revXlateYangtoConfigDB(self, yangJ, cDbJson): + + yangJ = self.xlateJson + cDbJson = self.revXlateJson + + # find table in config DB, use name as a KEY + for module_top in yangJ.keys(): + # module _top will be of from module:top + for container in yangJ[module_top].keys(): + #table = container.split(':')[1] + table = container + #print("revXlate " + table) + cmap = self.confDbYangMap[table] + cDbJson[table] = dict() + #print(key + "--" + subkey) + self.revXlateContainer(cmap['container'], yangJ[module_top][container], \ + cDbJson[table], table) + + return + +""" +Reverse Translate tp config DB +""" +def revXlateConfigDB(self, revXlateFile=None): + + yangJ = self.xlateJson + cDbJson = self.revXlateJson + # xlation is written in self.xlateJson + self.revXlateYangtoConfigDB(yangJ, cDbJson) + + if revXlateFile: + with open(revXlateFile, 'w') as f: + dump(self.revXlateJson, f, indent=4) + + return + +""" +Find a list in YANG Container +c = container +l = list name +return: list if found else None +""" +def findYangList(self, container, listName): + + if isinstance(container['list'], dict): + clist = container['list'] + if clist['@name'] == listName: + return clist + + elif isinstance(container['list'], list): + clist = [l for l in container['list'] if l['@name'] == listName] + return clist[0] + + return None + +""" +Find xpath of the PORT Leaf in PORT container/list. Xpath of Leaf is needed, +because only leaf can have leafrefs depend on them. +""" +def findXpathPortLeaf(self, portName): + + try: + table = "PORT" + xpath = self.findXpathPort(portName) + module, topc, container = self.get_module_TLC_container(table) + list = self.findYangList(container, table+"_LIST") + xpath = xpath + "/" + list['key']['@value'].split()[0] + except Exception as e: + print("find xpath of port Leaf failed") + raise e + + return xpath + + +""" +Find xpath of PORT +""" +def findXpathPort(self, portName): + + try: + table = "PORT" + module, topc, container = self.get_module_TLC_container(table) + xpath = "/" + module + ":" + topc + "/" + table + + list = self.findYangList(container, table+"_LIST") + xpath = self.findXpathList(xpath, list, [portName]) + except Exception as e: + print("find xpath of port failed") + raise e + + return xpath + +""" +Find xpath of a YANG LIST from keys, +xpath: xpath till list +list: YANG List +keys: list of keys in YANG LIST +""" +def findXpathList(self, xpath, list, keys): + + try: + # add list name in xpath + xpath = xpath + "/" + list['@name'] + listKeys = list['key']['@value'].split() + i = 0; + for listKey in listKeys: + xpath = xpath + '['+listKey+'=\''+keys[i]+'\']' + i = i + 1 + except Exception as e: + print("find xpath of list failed") + raise e + + return xpath + +""" +load_data: load Config DB, crop, xlate and create data tree from it. +input: data +returns: True - success False - failed +""" +def load_data(self, configdbJson): + + try: + self.jIn = configdbJson + # reset xlate + self.xlateJson = dict() + # self.jIn will be cropped + self.cropConfigDB("cropped.json") + # xlated result will be in self.xlateJson + self.xlateConfigDB() + #print(self.xlateJson) + self.root = self.ctx.parse_data_mem(dumps(self.xlateJson), \ + ly.LYD_JSON, ly.LYD_OPT_CONFIG|ly.LYD_OPT_STRICT) + + except Exception as e: + self.root = None + print("Data Loading Failed") + raise e + + return True + +""" +Get data from Data tree, data tree will be assigned in self.xlateJson +""" +def get_data(self): + + try: + self.xlateJson = loads(self.print_data_mem('JSON')) + # reset reverse xlate + self.revXlateJson = dict() + # result will be stored self.revXlateJson + self.revXlateConfigDB() + + except Exception as e: + print("Get Data Tree Failed") + raise e + + return self.revXlateJson + +""" +Delete a node from data tree, if this is LEAF and KEY Delete the Parent +""" +def delete_node(self, xpath): + + # These MACROS used only here, can we get it from Libyang Header ? + LYS_LEAF = 4 + node = self.find_data_node(xpath) + if node is None: + raise('Node {} not found'.format(xpath)) + + snode = node.schema() + # check for a leaf if it is a key. If yes delete the parent + if (snode.nodetype() == LYS_LEAF): + leaf = ly.Schema_Node_Leaf(snode) + if leaf.is_key(): + # try to delete parent + nodeP = self.find_parent_node(xpath) + xpathP = nodeP.path() + return self._delete_node(xpath=xpathP, node=nodeP) + else: + return self._delete_node(xpath=xpath, node=node) + + return True + +# End of class sonic_yang diff --git a/src/sonic-yang-mgmt/setup.py b/src/sonic-yang-mgmt/setup.py index e98e82129fd6..aeb0a3ea7ce0 100644 --- a/src/sonic-yang-mgmt/setup.py +++ b/src/sonic-yang-mgmt/setup.py @@ -53,13 +53,13 @@ def run (self): else: print("YANG Tests passed\n") - # Continue usual build steps # run pytest for libyang python APIs self.pytest_args = [] errno = pytest.main(self.pytest_args) if (errno): exit(errno) + # Continue usual build steps build_py.run(self) setup( @@ -89,7 +89,7 @@ def run (self): include_package_data=True, keywords='sonic_yang_mgmt', name='sonic_yang_mgmt', - py_modules=['sonic_yang'], + py_modules=['sonic_yang', '_sonic_yang_ext'], packages=find_packages(), setup_requires=setup_requirements, version='1.0', diff --git a/src/sonic-yang-mgmt/sonic_yang.py b/src/sonic-yang-mgmt/sonic_yang.py index 8e82f68d4726..5e52a16c2be1 100644 --- a/src/sonic-yang-mgmt/sonic_yang.py +++ b/src/sonic-yang-mgmt/sonic_yang.py @@ -1,7 +1,6 @@ import yang as ly -import sys -import json -import glob +from json import dump +from glob import glob """ Yang schema and data tree python APIs based on libyang python @@ -13,14 +12,32 @@ def __init__(self, yang_dir): self.module = None self.root = None + # yang model files, need this map it to module + self.yangFiles = list() + # map from TABLE in config DB to container and module + self.confDbYangMap = dict() + # JSON format of yang model [similar to pyang conversion] + self.yJson = list() + # config DB json input, will be cropped as yang models + self.jIn = dict() + # YANG JSON, this is traslated from config DB json + self.xlateJson = dict() + # reverse translation from yang JSON, == config db json + self.revXlateJson = dict() + try: self.ctx = ly.Context(yang_dir) except Exception as e: - self.fail(e) + self.fail(e) def fail(self, e): - print(e) - raise e + print(e) + raise e + + """ + import all function from extension file + """ + from _sonic_yang_ext import * """ load_schema_module(): load a Yang model file @@ -32,7 +49,7 @@ def load_schema_module(self, yang_file): self.ctx.parse_module_path(yang_file, ly.LYS_IN_YANG) except Exception as e: print("Failed to load yang module file: " + yang_file) - self.fail(e) + self.fail(e) """ load_schema_module_list(): load all Yang model files in the list @@ -52,7 +69,7 @@ def load_schema_module_list(self, yang_files): returns: Exception if error """ def load_schema_modules(self, yang_dir): - py = glob.glob(yang_dir+"/*.yang") + py = glob(yang_dir+"/*.yang") for file in py: try: self.load_schema_module(file) @@ -70,7 +87,7 @@ def load_schema_modules_ctx(self, yang_dir=None): ctx = ly.Context(yang_dir) - py = glob.glob(yang_dir+"/*.yang") + py = glob(yang_dir+"/*.yang") for file in py: try: ctx.parse_module_path(str(file), ly.LYS_IN_YANG) @@ -150,8 +167,7 @@ def print_data_mem (self, option): else: mem = self.root.print_mem(ly.LYD_XML, ly.LYP_WITHSIBLINGS | ly.LYP_FORMAT) - print("======================= print data =================") - print(mem) + return mem """ save_data_file_json(): save the data tree in memory into json file @@ -160,7 +176,7 @@ def print_data_mem (self, option): def save_data_file_json(self, outfile): mem = self.root.print_mem(ly.LYD_JSON, ly.LYP_FORMAT) with open(outfile, 'w') as out: - json.dump(mem, out, indent=4) + dump(mem, out, indent=4) """ get_module_tree(): get yang module tree in JSON or XMAL format @@ -366,19 +382,27 @@ def merge_data(self, data_file, yang_dir=None): self.fail(e) """ - delete_node(): delete a node from the schema/data tree + _delete_node(): delete a node from the schema/data tree, internal function input: xpath of the schema/data node returns: True - success False - failed """ - def delete_node(self, data_xpath): - try: - node = self.find_data_node(data_xpath) - except Exception as e: - print("Failed to delete data node for xpath: " + str(data_xpath)) - self.fail(e) + def _delete_node(self, xpath=None, node=None): + if node is None: + node = self.find_data_node(xpath) + + if (node): + node.unlink() + dnode = self.find_data_node(xpath) + if (dnode is None): + #deleted node not found + return True + else: + print('Could not delete Node') + return False else: - if (node): - node.unlink() + print("failed to find node, xpath: " + xpath) + + return False """ find_node_value(): find the value of a node from the schema/data tree @@ -487,4 +511,3 @@ def find_data_dependencies (self, data_xpath): ref_list.append(data_set.path()) return ref_list - diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/sample_config_db.json b/src/sonic-yang-mgmt/tests/libyang-python-tests/sample_config_db.json new file mode 100644 index 000000000000..a37639fb4b73 --- /dev/null +++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/sample_config_db.json @@ -0,0 +1,648 @@ +{ + "VLAN_INTERFACE": { + "Vlan111|2a04:5555:45:6709::1/64": { + "scope": "global", + "family": "IPv6" + }, + "Vlan111|10.222.10.65/26": { + "scope": "global", + "family": "IPv4" + }, + "Vlan111|fe80::1/10": { + "scope": "local", + "family": "IPv6" + }, + "Vlan777|2a04:5555:41:4e9::1/64": { + "scope": "global", + "family": "IPv6" + }, + "Vlan777|10.111.58.65/26": { + "scope": "global", + "family": "IPv4" + }, + "Vlan777|fe80::1/10": { + "scope": "local", + "family": "IPv6" + } + }, + "ACL_RULE": { + "V4-ACL-TABLE|DEFAULT_DENY": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "IPv4ANY", + "PRIORITY": "0" + }, + "V4-ACL-TABLE|Rule_20": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.72.0/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777780", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_40": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.72.64/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777760", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_60": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.80.0/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777740", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_80": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.80.64/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777720", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_111": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.152.17.52/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777700", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_120": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.252.208.41/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777880", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_140": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.148.128.245/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777860", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_160": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.1.245/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777840", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_180": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.252.222.21/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777820", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_9000": { + "PACKET_ACTION": "DROP", + "DST_IP": "0.0.0.0/0", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "991110", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_11100": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "0.0.0.0/0", + "SRC_IP": "0.0.0.0/0", + "PRIORITY": "990000", + "IP_TYPE": "IPv4ANY" + }, + "V6-ACL-TBLE|DEFAULT_DENY": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "IPv6ANY", + "PRIORITY": "0" + }, + "V6-ACL-TBLE|Rule_20": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777780", + "DST_IPV6": "2a04:5555:43:320::/64" + }, + "V6-ACL-TBLE|Rule_40": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777760", + "DST_IPV6": "2a04:5555:43:321::/64" + }, + "V6-ACL-TBLE|Rule_60": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777740", + "DST_IPV6": "2a04:5555:43:340::/64" + }, + "V6-ACL-TBLE|Rule_80": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777720", + "DST_IPV6": "2a04:5555:43:341::/64" + }, + "V6-ACL-TBLE|Rule_111": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777700", + "DST_IPV6": "2a04:5555:32:12::/64" + }, + "V6-ACL-TBLE|Rule_9000": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "991110", + "DST_IPV6": "::/0" + }, + "V6-ACL-TBLE|Rule_11100": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "::/0", + "PRIORITY": "990000", + "DST_IPV6": "::/0" + } + }, + "DEVICE_METADATA": { + "localhost": { + "mg_type": "ToR", + "mac": "00:11:22:33:dd:5a", + "hostname": "asw.dc", + "bgp_asn": "64850", + "hwsku": "Stone" + } + }, + "VLAN": { + "Vlan111": { + "description": "svlan", + "dhcp_servers": [ + "10.222.72.116" + ], + "vlanid": "111", + "mtu": "9216", + "admin_status": "up", + "members": [ + "Ethernet8", + "Ethernet3", + "Ethernet0", + "Ethernet1", + "Ethernet6", + "Ethernet4", + "Ethernet5", + "Ethernet9", + "Ethernet2", + "Ethernet7", + "Ethernet32", + "Ethernet30", + "Ethernet31", + "Ethernet36", + "Ethernet34", + "Ethernet33", + "Ethernet35", + "Ethernet29", + "Ethernet21", + "Ethernet20", + "Ethernet23", + "Ethernet22", + "Ethernet27", + "Ethernet26", + "Ethernet18", + "Ethernet19", + "Ethernet14", + "Ethernet15", + "Ethernet16", + "Ethernet17", + "Ethernet10", + "Ethernet11", + "Ethernet12", + "Ethernet13", + "Ethernet28" + ] + }, + "Vlan777": { + "description": "pvlan", + "dhcp_servers": [ + "10.222.72.116" + ], + "vlanid": "777", + "mtu": "9216", + "admin_status": "up", + "members": [ + "Ethernet9", + "Ethernet2", + "Ethernet8", + "Ethernet27", + "Ethernet14", + "Ethernet35" + ] + } + }, + "DEVICE_NEIGHBOR": { + "Ethernet112": { + "name": "dccsw01.nw", + "port": "Eth18" + }, + "Ethernet114": { + "name": "dccsw02.nw", + "port": "Eth18" + }, + "Ethernet116": { + "name": "dccsw03.nw", + "port": "Eth18" + }, + "Ethernet118": { + "name": "dccsw04.nw", + "port": "Eth18" + } + }, + "PORT": { + "Ethernet0": { + "alias": "Eth1/1", + "lanes": "65", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet1": { + "alias": "Eth1/2", + "lanes": "66", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet2": { + "alias": "Eth1/3", + "lanes": "67", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet3": { + "alias": "Eth1/4", + "lanes": "68", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet4": { + "alias": "Eth2/1", + "lanes": "69", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet5": { + "alias": "Eth2/2", + "lanes": "70", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet6": { + "alias": "Eth2/3", + "lanes": "71", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet7": { + "alias": "Eth2/4", + "lanes": "72", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet8": { + "alias": "Eth3/1", + "lanes": "73", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet9": { + "alias": "Eth3/2", + "lanes": "74", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet10": { + "alias": "Eth3/3", + "lanes": "75", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet11": { + "alias": "Eth3/4", + "lanes": "76", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet12": { + "alias": "Eth4/1", + "lanes": "77", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet13": { + "alias": "Eth4/2", + "lanes": "78", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet14": { + "alias": "Eth4/3", + "lanes": "79", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet15": { + "alias": "Eth4/4", + "lanes": "80", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet16": { + "alias": "Eth5/1", + "lanes": "33", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet17": { + "alias": "Eth5/2", + "lanes": "34", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet18": { + "alias": "Eth5/3", + "lanes": "35", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet19": { + "alias": "Eth5/4", + "lanes": "36", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet20": { + "alias": "Eth6/1", + "lanes": "37", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet21": { + "alias": "Eth6/2", + "lanes": "38", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet22": { + "alias": "Eth6/3", + "lanes": "39", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet23": { + "alias": "Eth6/4", + "lanes": "40", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet24": { + "alias": "Eth7/1", + "lanes": "41", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet25": { + "alias": "Eth7/2", + "lanes": "42", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet26": { + "alias": "Eth7/3", + "lanes": "43", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet27": { + "alias": "Eth7/4", + "lanes": "44", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet28": { + "alias": "Eth8/1", + "lanes": "45", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet29": { + "alias": "Eth8/2", + "lanes": "46", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet30": { + "alias": "Eth8/3", + "lanes": "47", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet31": { + "alias": "Eth8/4", + "lanes": "48", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet32": { + "alias": "Eth9/1", + "lanes": "49", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet33": { + "alias": "Eth9/2", + "lanes": "50", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet34": { + "alias": "Eth9/3", + "lanes": "51", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet35": { + "alias": "Eth9/4", + "lanes": "52", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet36": { + "alias": "Eth10/1", + "lanes": "53", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet112": { + "alias": "Eth29/1", + "lanes": "113,114", + "description": "50G|dccsw01.nw|Eth18", + "fec": "fc", + "admin_status": "up" + } + }, + "ACL_TABLE": { + "V4-ACL-TABLE": { + "type": "L3", + "policy_desc": "V4-ACL-TABLE", + "ports": [ + "Ethernet26", + "Ethernet27", + "Ethernet24" + ] + }, + "V6-ACL-TBLE": { + "type": "L3V6", + "policy_desc": "V6-ACL-TBLE", + "ports": [ + "Ethernet14", + "Ethernet15", + "Ethernet23", + "Ethernet30", + "Ethernet31", + "Ethernet18", + "Ethernet19", + "Ethernet25", + "Ethernet24" + ] + } + }, + "INTERFACE": { + "Ethernet112|2a04:5555:40:a709::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet112|10.184.228.211/31": { + "scope": "global", + "family": "IPv4" + }, + "Ethernet14|2a04:5555:40:a749::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet14|10.184.229.211/31": { + "scope": "global", + "family": "IPv4" + }, + "Ethernet16|2a04:5555:40:a789::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet16|10.184.230.211/31": { + "scope": "global", + "family": "IPv4" + }, + "Ethernet18|2a04:5555:40:a7c9::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet18|10.184.231.211/31": { + "scope": "global", + "family": "IPv4" + } + }, + "VLAN_MEMBER": { + "Vlan111|Ethernet0": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet1": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet2": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet3": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet4": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet5": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet6": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet29": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet30": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet31": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet32": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet33": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet34": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet35": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet36": { + "tagging_mode": "untagged" + } + }, + "LOOPBACK_INTERFACE": { + "Loopback0|2a04:5555:40:4::4e9/128": { + "scope": "global", + "family": "IPv6" + }, + "Loopback0|10.184.8.233/32": { + "scope": "global", + "family": "IPv4" + } + }, + "CRM": { + "Config": { + "polling_interval": "0" + } + } +} diff --git a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py index 15d68a028b2b..dc7fc268179a 100644 --- a/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py +++ b/src/sonic-yang-mgmt/tests/libyang-python-tests/test_sonic_yang.py @@ -7,12 +7,16 @@ import getopt import subprocess import glob +from ijson import items as ijson_itmes test_path = os.path.dirname(os.path.abspath(__file__)) modules_path = os.path.dirname(test_path) sys.path.insert(0, modules_path) class Test_SonicYang(object): + # class vars + yang_test_file = "/sonic/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json" + @pytest.fixture(autouse=True, scope='class') def data(self): test_file = "/sonic/src/sonic-yang-mgmt/tests/libyang-python-tests/test_SonicYang.json" @@ -34,6 +38,25 @@ def jsonTestParser(self, file): data = json.load(data_file) return data + """ + Get the JSON input based on func name + and return jsonInput + """ + def readIjsonInput(self, test): + try: + # load test specific Dictionary, using Key = func + # this is to avoid loading very large JSON in memory + print(" Read JSON Section: " + test) + jInput = "" + with open(self.yang_test_file, 'rb') as f: + jInst = ijson_itmes(f, test) + for it in jInst: + jInput = jInput + json.dumps(it) + except Exception as e: + print("Reading Ijson failed") + raise(e) + return jInput + def setup_class(cls): pass @@ -132,7 +155,7 @@ def test_delete_node(self, data, yang_s): for node in data['delete_nodes']: expected = node['valid'] xpath = str(node['xpath']) - yang_s.delete_node(xpath) + yang_s._delete_node(xpath) #test set node's value def test_set_datanode_value(self, data, yang_s): @@ -191,5 +214,33 @@ def test_merge_data_tree(self, data, yang_s): yang_s.merge_data(data_merge_file, yang_dir) #yang_s.root.print_mem(ly.LYD_JSON, ly.LYP_FORMAT) + def test_xlate_rev_xlate(self): + # This Test is with Sonic YANG model, so create class from start + # read the config + yang_dir = "/sonic/src/sonic-yang-mgmt/yang-models/" + jIn = self.readIjsonInput('SAMPLE_CONFIG_DB_JSON') + # load yang models + syc = sy.sonic_yang(yang_dir) + + syc.loadYangModel() + + syc.load_data(json.loads(jIn)) + + syc.get_data() + + if syc.jIn == syc.revXlateJson: + print("Xlate and Rev Xlate Passed") + else: + # Right now, interface and vlan_interface will have default diff due to ip_prefix + from jsondiff import diff + configDiff = diff(syc.jIn, syc.revXlateJson, syntax='symmetric') + for key in configDiff.keys(): + if 'INTERFACE' not in key: + print("Xlate and Rev Xlate failed") + sys.exit(1) + print("Xlate and Rev Xlate Passed") + + return + def teardown_class(cls): pass diff --git a/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json b/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json index 30ff17aa1f33..0e79baf09b57 100644 --- a/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json +++ b/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json @@ -521,5 +521,654 @@ ] } } + }, + + "SAMPLE_CONFIG_DB_JSON": { + "VLAN_INTERFACE": { + "Vlan111|2a04:5555:45:6709::1/64": { + "scope": "global", + "family": "IPv6" + }, + "Vlan111|10.222.10.65/26": { + "scope": "global", + "family": "IPv4" + }, + "Vlan111|fe80::1/10": { + "scope": "local", + "family": "IPv6" + }, + "Vlan777|2a04:5555:41:4e9::1/64": { + "scope": "global", + "family": "IPv6" + }, + "Vlan777|10.111.58.65/26": { + "scope": "global", + "family": "IPv4" + }, + "Vlan777|fe80::1/10": { + "scope": "local", + "family": "IPv6" + } + }, + "ACL_RULE": { + "V4-ACL-TABLE|DEFAULT_DENY": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "IPv4ANY", + "PRIORITY": "0" + }, + "V4-ACL-TABLE|Rule_20": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.72.0/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777780", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_40": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.72.64/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777760", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_60": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.80.0/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777740", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_80": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.80.64/26", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777720", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_111": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.152.17.52/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777700", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_120": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.252.208.41/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777880", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_140": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.148.128.245/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777860", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_160": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.222.1.245/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777840", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_180": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.252.222.21/32", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "777820", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_9000": { + "PACKET_ACTION": "DROP", + "DST_IP": "0.0.0.0/0", + "SRC_IP": "10.222.0.0/15", + "PRIORITY": "991110", + "IP_TYPE": "IPv4ANY" + }, + "V4-ACL-TABLE|Rule_11100": { + "PACKET_ACTION": "FORWARD", + "DST_IP": "0.0.0.0/0", + "SRC_IP": "0.0.0.0/0", + "PRIORITY": "990000", + "IP_TYPE": "IPv4ANY" + }, + "V6-ACL-TBLE|DEFAULT_DENY": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "IPv6ANY", + "PRIORITY": "0" + }, + "V6-ACL-TBLE|Rule_20": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777780", + "DST_IPV6": "2a04:5555:43:320::/64" + }, + "V6-ACL-TBLE|Rule_40": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777760", + "DST_IPV6": "2a04:5555:43:321::/64" + }, + "V6-ACL-TBLE|Rule_60": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777740", + "DST_IPV6": "2a04:5555:43:340::/64" + }, + "V6-ACL-TBLE|Rule_80": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777720", + "DST_IPV6": "2a04:5555:43:341::/64" + }, + "V6-ACL-TBLE|Rule_111": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "777700", + "DST_IPV6": "2a04:5555:32:12::/64" + }, + "V6-ACL-TBLE|Rule_9000": { + "PACKET_ACTION": "DROP", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "2a04:5555:41::/48", + "PRIORITY": "991110", + "DST_IPV6": "::/0" + }, + "V6-ACL-TBLE|Rule_11100": { + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IPv6ANY", + "SRC_IPV6": "::/0", + "PRIORITY": "990000", + "DST_IPV6": "::/0" + } + }, + "DEVICE_METADATA": { + "localhost": { + "type": "ToR", + "mac": "00:11:22:33:dd:5a", + "hostname": "asw.dc", + "bgp_asn": "64850", + "hwsku": "Stone" + } + }, + "VLAN": { + "Vlan111": { + "description": "svlan", + "dhcp_servers": [ + "10.222.72.116" + ], + "vlanid": "111", + "mtu": "9216", + "admin_status": "up", + "members": [ + "Ethernet8", + "Ethernet3", + "Ethernet0", + "Ethernet1", + "Ethernet6", + "Ethernet4", + "Ethernet5", + "Ethernet9", + "Ethernet2", + "Ethernet7", + "Ethernet32", + "Ethernet30", + "Ethernet31", + "Ethernet36", + "Ethernet34", + "Ethernet33", + "Ethernet35", + "Ethernet29", + "Ethernet21", + "Ethernet20", + "Ethernet23", + "Ethernet22", + "Ethernet27", + "Ethernet26", + "Ethernet18", + "Ethernet19", + "Ethernet14", + "Ethernet15", + "Ethernet16", + "Ethernet17", + "Ethernet10", + "Ethernet11", + "Ethernet12", + "Ethernet13", + "Ethernet28" + ] + }, + "Vlan777": { + "description": "pvlan", + "dhcp_servers": [ + "10.222.72.116" + ], + "vlanid": "777", + "mtu": "9216", + "admin_status": "up", + "members": [ + "Ethernet9", + "Ethernet2", + "Ethernet8", + "Ethernet27", + "Ethernet14", + "Ethernet35" + ] + } + }, + "DEVICE_NEIGHBOR": { + "Ethernet112": { + "name": "dccsw01.nw", + "port": "Eth18" + }, + "Ethernet114": { + "name": "dccsw02.nw", + "port": "Eth18" + }, + "Ethernet116": { + "name": "dccsw03.nw", + "port": "Eth18" + }, + "Ethernet118": { + "name": "dccsw04.nw", + "port": "Eth18" + } + }, + "PORT": { + "Ethernet0": { + "alias": "Eth1/1", + "lanes": "65", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet1": { + "alias": "Eth1/2", + "lanes": "66", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet2": { + "alias": "Eth1/3", + "lanes": "67", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet3": { + "alias": "Eth1/4", + "lanes": "68", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet4": { + "alias": "Eth2/1", + "lanes": "69", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet5": { + "alias": "Eth2/2", + "lanes": "70", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet6": { + "alias": "Eth2/3", + "lanes": "71", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet7": { + "alias": "Eth2/4", + "lanes": "72", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet8": { + "alias": "Eth3/1", + "lanes": "73", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet9": { + "alias": "Eth3/2", + "lanes": "74", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet10": { + "alias": "Eth3/3", + "lanes": "75", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet11": { + "alias": "Eth3/4", + "lanes": "76", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet12": { + "alias": "Eth4/1", + "lanes": "77", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet13": { + "alias": "Eth4/2", + "lanes": "78", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet14": { + "alias": "Eth4/3", + "lanes": "79", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet15": { + "alias": "Eth4/4", + "lanes": "80", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet16": { + "alias": "Eth5/1", + "lanes": "33", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet17": { + "alias": "Eth5/2", + "lanes": "34", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet18": { + "alias": "Eth5/3", + "lanes": "35", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet19": { + "alias": "Eth5/4", + "lanes": "36", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet20": { + "alias": "Eth6/1", + "lanes": "37", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet21": { + "alias": "Eth6/2", + "lanes": "38", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet22": { + "alias": "Eth6/3", + "lanes": "39", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet23": { + "alias": "Eth6/4", + "lanes": "40", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet24": { + "alias": "Eth7/1", + "lanes": "41", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet25": { + "alias": "Eth7/2", + "lanes": "42", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet26": { + "alias": "Eth7/3", + "lanes": "43", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet27": { + "alias": "Eth7/4", + "lanes": "44", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet28": { + "alias": "Eth8/1", + "lanes": "45", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet29": { + "alias": "Eth8/2", + "lanes": "46", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet30": { + "alias": "Eth8/3", + "lanes": "47", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet31": { + "alias": "Eth8/4", + "lanes": "48", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet32": { + "alias": "Eth9/1", + "lanes": "49", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet33": { + "alias": "Eth9/2", + "lanes": "50", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet34": { + "alias": "Eth9/3", + "lanes": "51", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet35": { + "alias": "Eth9/4", + "lanes": "52", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet36": { + "alias": "Eth10/1", + "lanes": "53", + "description": "", + "speed": "11100", + "admin_status": "up" + }, + "Ethernet112": { + "alias": "Eth29/1", + "lanes": "113,114", + "description": "50G|dccsw01.nw|Eth18", + "fec": "fc", + "admin_status": "up" + } + }, + "ACL_TABLE": { + "V4-ACL-TABLE": { + "type": "L3", + "policy_desc": "V4-ACL-TABLE", + "ports": [ + "Ethernet26", + "Ethernet27", + "Ethernet24" + ] + }, + "V6-ACL-TBLE": { + "type": "L3V6", + "policy_desc": "V6-ACL-TBLE", + "ports": [ + "Ethernet14", + "Ethernet15", + "Ethernet23", + "Ethernet30", + "Ethernet31", + "Ethernet18", + "Ethernet19", + "Ethernet25", + "Ethernet24" + ] + } + }, + "INTERFACE": { + "Ethernet112|2a04:5555:40:a709::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet112|10.184.228.211/31": { + "scope": "global", + "family": "IPv4" + }, + "Ethernet14|2a04:5555:40:a749::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet14|10.184.229.211/31": { + "scope": "global", + "family": "IPv4" + }, + "Ethernet16|2a04:5555:40:a789::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet16|10.184.230.211/31": { + "scope": "global", + "family": "IPv4" + }, + "Ethernet18|2a04:5555:40:a7c9::2/126": { + "scope": "global", + "family": "IPv6" + }, + "Ethernet18|10.184.231.211/31": { + "scope": "global", + "family": "IPv4" + } + }, + "VLAN_MEMBER": { + "Vlan111|Ethernet0": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet1": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet2": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet3": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet4": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet5": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet6": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet29": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet30": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet31": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet32": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet33": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet34": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet35": { + "tagging_mode": "untagged" + }, + "Vlan111|Ethernet36": { + "tagging_mode": "untagged" + } + }, + "LOOPBACK_INTERFACE": { + "Loopback0|2a04:5555:40:4::4e9/128": { + "scope": "global", + "family": "IPv6" + }, + "Loopback0|10.184.8.233/32": { + "scope": "global", + "family": "IPv4" + } + }, + "CRM": { + "Config": { + "polling_interval": "0" + } + } } }