From 2a5ae21c60fcd4535292e54636c702932537cabe Mon Sep 17 00:00:00 2001 From: Praveen Chaudhary Date: Wed, 2 Oct 2019 20:42:20 -0700 Subject: [PATCH 1/2] [yang-model-tests]: YANG model test code and JSON input for testing. --- .../test_code/yangModelTesting.py | 67 --- .../yang-model-tests/yangModelTesting.py | 426 ++++++++++++++ .../tests/yang-model-tests/yangTest.json | 525 ++++++++++++++++++ 3 files changed, 951 insertions(+), 67 deletions(-) delete mode 100644 src/sonic-yang-mgmt/test_code/yangModelTesting.py create mode 100644 src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py create mode 100644 src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json diff --git a/src/sonic-yang-mgmt/test_code/yangModelTesting.py b/src/sonic-yang-mgmt/test_code/yangModelTesting.py deleted file mode 100644 index 8a20578c247e..000000000000 --- a/src/sonic-yang-mgmt/test_code/yangModelTesting.py +++ /dev/null @@ -1,67 +0,0 @@ -import yang as ly -import sonic_yang as sy -import logging - -from os import listdir -from os.path import isfile, join, splitext - -logging.basicConfig(level=logging.DEBUG) -log = logging.getLogger(":") -log.setLevel(logging.DEBUG) -log.addHandler(logging.NullHandler()) - -def main(): - - yangDir = "/sonic/src/sonic-yang-mgmt/models" - yangDataInst = yangDir + "/sonic_config_data.json" - - # get all files - yangFiles = [f for f in listdir(yangDir) if isfile(join(yangDir, f))] - # get all yang files - yangFiles = [f for f in yangFiles if splitext(f)[-1].lower()==".yang"] - yangFiles = [f.split('.')[0] for f in yangFiles] - - # load yang mdoules - ctx = ly.Context(yangDir) - for f in yangFiles: - # load a module m - m = ctx.get_module(f) - if m is not None: - log.error(m.name()) - else: - m = ctx.load_module(f) - if m is not None: - log.info("module: {} is loaded successfully".format(m.name())) - - try: - root = ctx.parse_data_path(yangDataInst, ly.LYD_JSON, ly.LYD_OPT_CONFIG | ly.LYD_OPT_STRICT) - - if root: - log.info("Tree DFS\n") - p = root.print_mem(ly.LYD_JSON, ly.LYP_WITHSIBLINGS | ly.LYP_FORMAT) - log.info("===================Data=================") - log.info(p) - - sYangInst = sy.sonic_yang(yangDir) - sYangInst.root = root - sYangInst.validate_data_tree(root, ctx) - - xpath = "/sonic-port:PORT/PORT_LIST[port_name='Ethernet1']/port_name" - #find_topo_sort_dependencies(sYangInst, xpath) - refs = sYangInst.find_data_dependencies(xpath) - printList(refs) - - except Exception as e: - print(e) - - -def printList(l): # list l - - print("list: ") - for item in l: - print (item) - - return - -if __name__ == '__main__': - main() \ No newline at end of file diff --git a/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py b/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py new file mode 100644 index 000000000000..7b51e69e8020 --- /dev/null +++ b/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py @@ -0,0 +1,426 @@ +import yang as ly +import logging +import argparse +import sys +import ijson +import json +#import sonic_yang as sy +from os import listdir +from os.path import isfile, join, splitext + +#Globals vars +PASS = 0 +FAIL = 1 +logging.basicConfig(level=logging.DEBUG) +log = logging.getLogger("YANG-TEST") +log.setLevel(logging.INFO) +log.addHandler(logging.NullHandler()) + +# Global functions +def printExceptionDetails(): + try: + excType, excObj, traceBack = sys.exc_info() + fileName = traceBack.tb_frame.f_code.co_filename + lineNumber = traceBack.tb_lineno + log.error(" Exception >{}< in {}:{}".format(excObj, fileName, lineNumber)) + except Exception as e: + log.error(" Exception in printExceptionDetails") + return + +# class for YANG Model YangModelTesting +# Run function will run all the tests +# from a user given list. + +class YangModelTesting: + + def __init__(self, tests, yangDir, jsonFile): + self.tests = tests + self.yangDir = yangDir + self.jsonFile = jsonFile + self.testNum = 1 + # other class vars + # self.ctx + return + + """ + load all YANG models before test run + """ + def loadYangModel(self, yangDir): + try: + # get all files + yangFiles = [f for f in listdir(yangDir) if isfile(join(yangDir, f))] + # get all yang files + yangFiles = [f for f in yangFiles if splitext(f)[-1].lower()==".yang"] + yangFiles = [f.split('.')[0] for f in yangFiles] + # load yang mdoules + self.ctx = ly.Context(yangDir) + log.debug(yangFiles) + for f in yangFiles: + # load a module + log.debug(f) + m = self.ctx.get_module(f) + if m is not None: + log.error("Could not get module: {}".format(m.name())) + else: + m = self.ctx.load_module(f) + if m is not None: + log.info("module: {} is loaded successfully".format(m.name())) + else: + return + except Exception as e: + printExceptionDetails() + raise e + return + + """ + Run all tests from list self.tests + """ + def run(self): + try: + self.loadYangModel(self.yangDir) + ret = 0 + for test in self.tests: + func = "yangTest" + test; + # Pass function name, needed to retrieve input Json Dictionary + ret = ret + getattr(self, func)(func) + except Exception as e: + printExceptionDetails() + raise e + return ret + + """ + Get the JSON input based on func name + and return jsonInput + """ + def readJsonInput(self, func): + try: + # load test specific Dictionary, using Key = func + # this is to avoid loading very large JSON in memory + log.debug(" Read JSON Section: " + func) + jInput = "" + with open(self.jsonFile, 'rb') as f: + jInst = ijson.items(f, func) + for it in jInst: + jInput = jInput + json.dumps(it) + log.debug(jInput) + except Exception as e: + printExceptionDetails() + return jInput + + """ + Log the start of a test + """ + def logStartTest(self, tStr): + log.info("\n------------------- Test "+ str(self.testNum) +\ + ": " + tStr + "---------------------") + self.testNum = self.testNum + 1 + return + + """ + Load Config Data and return Exception as String + """ + def loadConfigData(self, jInput): + s = "" + try: + node = self.ctx.parse_data_mem(jInput, ly.LYD_JSON, \ + ly.LYD_OPT_CONFIG | ly.LYD_OPT_STRICT) + except Exception as e: + s = str(e) + log.debug(s) + return s + + """ + Test 1: Configure Wrong family with ip-prefix for VLAN_Interface Table. + """ + def yangTest1(self, func): + try: + tStr = " Configure Wrong family with ip-prefix for VLAN_Interface Table." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Must condition" in s and "not satisfied" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Test 2: Add dhcp_server which is not in correct ip-prefix format. + """ + def yangTest2(self, func): + try: + tStr = " Add dhcp_server which is not in correct ip-prefix format." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Invalid value" in s and "dhcp_servers" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure a member port in VLAN_MEMBER table which does not exist. + """ + def yangTest3(self, func): + try: + tStr = " Configure a member port in VLAN_MEMBER table which does not exist." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Leafref" in s and "non-existing" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure vlan-id in VLAN_MEMBER table which does not exist in VLAN table. + """ + def yangTest4(self, func): + try: + tStr = " Configure vlan-id in VLAN_MEMBER table which does not exist in VLAN table." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Leafref" in s and "non-existing" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure wrong value for tagging_mode. + """ + def yangTest5(self, func): + try: + tStr = " Configure wrong value for tagging_mode." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Invalid value" in s and "tagging_mode" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure empty string as ip-prefix in INTERFACE table. + """ + def yangTest6(self, func): + try: + tStr = " Configure empty string as ip-prefix in INTERFACE table." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Invalid value" in s and "ip-prefix" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure undefined packet_action in ACL_RULE table. + """ + def yangTest7(self, func): + try: + tStr = " Configure undefined packet_action in ACL_RULE table." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Invalid value" in s and "PACKET_ACTION" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure undefined acl_table_type in ACL_TABLE table. + """ + def yangTest8(self, func): + try: + tStr = " Configure undefined acl_table_type in ACL_TABLE table." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Invalid value" in s and "type" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure non-existing ACL_TABLE in ACL_RULE. + """ + def yangTest9(self, func): + try: + tStr = " Configure non-existing ACL_TABLE in ACL_RULE." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("Leafref" in s and "non-existing" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure IP_TYPE as ipv4any and SRC_IPV6 in ACL_RULE. + """ + def yangTest10(self, func): + try: + tStr = " Configure IP_TYPE as ipv4any and SRC_IPV6 in ACL_RULE." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("When condition" in s and "not satisfied" in s and "IP_TYPE" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure IP_TYPE as ARP and DST_IPV6 in ACL_RULE. + """ + def yangTest11(self, func): + try: + tStr = " Configure IP_TYPE as ARP and DST_IPV6 in ACL_RULE." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("When condition" in s and "not satisfied" in s and "IP_TYPE" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure l4_src_port_range as 99999-99999 in ACL_RULE. + """ + def yangTest12(self, func): + try: + tStr = " Configure l4_src_port_range as 99999-99999 in ACL_RULE." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("does not satisfy" in s and "pattern" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + """ + Configure IP_TYPE as ARP and ICMPV6_CODE in ACL_RULE. + """ + def yangTest13(self, func): + try: + tStr = " Configure IP_TYPE as ARP and ICMPV6_CODE in ACL_RULE." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("When condition" in s and "not satisfied" in s and "IP_TYPE" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + + + """ + Configure INNER_ETHER_TYPE as 0x080C in ACL_RULE. + """ + def yangTest14(self, func): + try: + tStr = " Configure INNER_ETHER_TYPE as 0x080C in ACL_RULE." + self.logStartTest(tStr) + jInput = self.readJsonInput(func) + # load the data, expect a exception with must condition failure + s = self.loadConfigData(jInput) + if ("does not satisfy" in s and "pattern" in s): + log.info(tStr + " Passed\n") + return PASS + except Exception as e: + printExceptionDetails() + log.info(tStr + " Failed\n") + return FAIL + +# End of Class + +""" + Start Here +""" +def main(): + parser = argparse.ArgumentParser(description='Script to run YANG model tests', + formatter_class=argparse.RawTextHelpFormatter, + epilog=""" +Usage: +python yangModelTesting.py -h +""") + parser.add_argument('-t', '--tests', type=str, \ + help='Tests to run, Format 1,2,3', required=True) + parser.add_argument('-f', '--json-file', type=str, \ + help='JSON input for tests ', required=True) + parser.add_argument('-y', '--yang-dir', type=str, \ + help='Path to YANG models', required=True) + parser.add_argument('-v', '--verbose-level', type=str, \ + help='Verbose Level of Logs', choices=['INFO', 'DEBUG']) + args = parser.parse_args() + try: + tests = args.tests + jsonFile = args.json_file + yangDir = args.yang_dir + logLevel = args.verbose_level + if logLevel == "DEBUG": + log.setLevel(logging.DEBUG) + # Make a list + tests = tests.split(",") + yTest = YangModelTesting(tests, yangDir, jsonFile) + ret = yTest.run() + if ret == 0: + log.info("All Test Passed") + sys.exit(ret) + except Exception as e: + printExceptionDetails() + sys.exit(1) + + return +if __name__ == '__main__': + main() diff --git a/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json b/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json new file mode 100644 index 000000000000..e780ced82067 --- /dev/null +++ b/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json @@ -0,0 +1,525 @@ +{ + "yangTest1": { + "sonic-vlan:sonic-vlan": { + "sonic-vlan:VLAN_INTERFACE": { + "VLAN_INTERFACE_LIST": [{ + "vlanid": 100, + "ip-prefix": "2a04:5555:66:7777::1/64", + "scope": "global", + "family": "IPv4" + }] + }, + "sonic-vlan:VLAN": { + "VLAN_LIST": [{ + "vlanid": 100, + "description": "server_vlan" + }] + } + } + }, + + "yangTest2": { + "sonic-vlan:sonic-vlan": { + "sonic-vlan:VLAN": { + "VLAN_LIST": [{ + "vlanid": 100, + "description": "server_vlan", + "dhcp_servers": [ + "10.186.72.566" + ], + "mtu": "9216", + "admin_status": "up" + }] + } + } + }, + + "yangTest3": { + "sonic-vlan:sonic-vlan": { + "sonic-vlan:VLAN_MEMBER": { + "VLAN_MEMBER_LIST": [{ + "vlanid": 100, + "port": "Ethernet156", + "tagging_mode": "tagged" + }] + }, + "sonic-vlan:VLAN": { + "VLAN_LIST": [{ + "vlanid": 100, + "description": "server_vlan" + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest4": { + "sonic-vlan:sonic-vlan": { + "sonic-vlan:VLAN_MEMBER": { + "VLAN_MEMBER_LIST": [{ + "vlanid": 200, + "port": "Ethernet0", + "tagging_mode": "tagged" + }] + }, + "sonic-vlan:VLAN": { + "VLAN_LIST": [{ + "vlanid": 100, + "description": "server_vlan" + }, + { + "vlanid": 300, + "description": "ipmi_vlan" + } + ] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }] + } + } + }, + + "yangTest5": { + "sonic-vlan:sonic-vlan": { + "sonic-vlan:VLAN_MEMBER": { + "VLAN_MEMBER_LIST": [{ + "vlanid": 100, + "port": "Ethernet0", + "tagging_mode": "non-tagged" + }] + }, + "sonic-vlan:VLAN": { + "VLAN_LIST": [{ + "vlanid": 100, + "description": "server_vlan" + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }] + } + } + }, + + "yangTest6": { + "sonic-interface:sonic-interface": { + "sonic-interface:INTERFACE": { + "INTERFACE_LIST": [{ + "interface": "Ethernet8", + "ip-prefix": "", + "scope": "global", + "family": "IPv4" + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet8", + "alias": "eth8", + "description": "Ethernet8", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }] + } + } + }, + + "yangTest7": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "RULE_NAME": "Rule_20", + "PACKET_ACTION": "SEND", + "DST_IP": "10.186.72.0/26", + "SRC_IP": "10.176.0.0/15", + "PRIORITY": 999980, + "IP_TYPE": "IPV4ANY" + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "policy_desc": "Filter IPv4", + "type": "L3", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest8": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V6", + "policy_desc": "Filter IPv6", + "type": "LAYER3V4", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest9": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NOT-EXIST", + "RULE_NAME": "Rule_20", + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.186.72.0/26", + "SRC_IP": "10.176.0.0/15", + "PRIORITY": 999980, + "IP_TYPE": "IPV4ANY" + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "policy_desc": "Filter IPv6", + "type": "L3", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest10": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "RULE_NAME": "Rule_20", + "PACKET_ACTION": "FORWARD", + "SRC_IPV6": "2001::1/64", + "PRIORITY": 999980, + "IP_TYPE": "IPV4ANY" + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "policy_desc": "Filter IPv4", + "type": "L3", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest11": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V6", + "RULE_NAME": "Rule_20", + "PACKET_ACTION": "FORWARD", + "DST_IPV6": "2001::2/64", + "PRIORITY": 999980, + "IP_TYPE": "ARP" + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V6", + "policy_desc": "Filter IPv6", + "type": "L3V6", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest12": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V6", + "RULE_NAME": "Rule_20", + "PACKET_ACTION": "FORWARD", + "IP_TYPE": "IP", + "SRC_IPV6": "2a04:f547:41::/48", + "PRIORITY": 999980, + "DST_IPV6": "2a04:f547:43:320::/64", + "L4_SRC_PORT_RANGE": "99999-99999" + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V6", + "policy_desc": "Filter IPv6", + "type": "L3V6", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest13": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "RULE_NAME": "Rule_40", + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.186.72.64/26", + "SRC_IP": "10.176.0.0/15", + "PRIORITY": 999960, + "ICMPV6_CODE": 5, + "IP_TYPE": "ARP", + "INNER_ETHER_TYPE": "0x88CC" + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "policy_desc": "Filter IPv4", + "type": "L3", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + }, + + "yangTest14": { + "sonic-acl:sonic-acl": { + "sonic-acl:ACL_RULE": { + "ACL_RULE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "RULE_NAME": "Rule_40", + "PACKET_ACTION": "FORWARD", + "DST_IP": "10.186.72.64/26", + "SRC_IP": "10.176.0.0/15", + "PRIORITY": 999960, + "IP_TYPE": "ARP", + "INNER_ETHER_TYPE": "0x080C" + + }] + }, + "sonic-acl:ACL_TABLE": { + "ACL_TABLE_LIST": [{ + "ACL_TABLE_NAME": "NO-NSW-PACL-V4", + "policy_desc": "Filter IPv4", + "type": "L3", + "stage": "EGRESS", + "ports": ["Ethernet0", "Ethernet1"] + }] + } + }, + "sonic-port:sonic-port": { + "sonic-port:PORT": { + "PORT_LIST": [{ + "port_name": "Ethernet0", + "alias": "eth0", + "description": "Ethernet0", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + }, + { + "port_name": "Ethernet1", + "alias": "eth1", + "description": "Ethernet1", + "speed": 25000, + "mtu": 9000, + "admin_status": "up" + } + ] + } + } + } +} From 645120194d0b3a8585bbea815f44410c2e63adb8 Mon Sep 17 00:00:00 2001 From: Praveen Chaudhary Date: Tue, 8 Oct 2019 12:41:21 -0700 Subject: [PATCH 2/2] [yang-model-tests]: Edited YANG model test code with map for Error and Tests. --- .../yang-model-tests/yangModelTesting.py | 361 +++++------------- .../tests/yang-model-tests/yangTest.json | 28 +- 2 files changed, 119 insertions(+), 270 deletions(-) diff --git a/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py b/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py index 7b51e69e8020..ca19c22c315e 100644 --- a/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py +++ b/src/sonic-yang-mgmt/tests/yang-model-tests/yangModelTesting.py @@ -1,3 +1,5 @@ +# This script is used to + import yang as ly import logging import argparse @@ -34,7 +36,76 @@ def printExceptionDetails(): class YangModelTesting: def __init__(self, tests, yangDir, jsonFile): + self.defaultYANGFailure = { + 'Must': ['Must condition', 'not satisfied'], + 'InvalidValue': ['Invalid value'], + 'LeafRef': ['Leafref', 'non-existing'], + 'When': ['When condition', 'not satisfied'], + 'Pattern': ['pattern', 'does not satisfy'] + } + + self.ExceptionTests = { + 'WRONG_FAMILY_WITH_IP_PREFIX': { + 'desc': 'Configure Wrong family with ip-prefix for VLAN_Interface Table', + 'eStr': self.defaultYANGFailure['Must'] + }, + 'DHCP_SERVER_INCORRECT_FORMAT': { + 'desc': 'Add dhcp_server which is not in correct ip-prefix format.', + 'eStr': self.defaultYANGFailure['InvalidValue'] + ['dhcp_servers'] + }, + 'VLAN_WITH_NON_EXIST_PORT': { + 'desc': 'Configure a member port in VLAN_MEMBER table which does not exist.', + 'eStr': self.defaultYANGFailure['LeafRef'] + }, + 'VLAN_MEMEBER_WITH_NON_EXIST_VLAN': { + 'desc': 'Configure vlan-id in VLAN_MEMBER table which does not exist in VLAN table.', + 'eStr': self.defaultYANGFailure['LeafRef'] + }, + 'TAGGING_MODE_WRONG_VALUE': { + 'desc': 'Configure wrong value for tagging_mode.', + 'eStr': self.defaultYANGFailure['InvalidValue'] + ['tagging_mode'] + }, + 'INTERFACE_IP_PREFIX_EMPTY_STRING': { + 'desc': 'Configure empty string as ip-prefix in INTERFACE table.', + 'eStr': self.defaultYANGFailure['InvalidValue'] + ['ip-prefix'] + }, + 'ACL_RULE_UNDEFINED_PACKET_ACTION': { + 'desc': 'Configure undefined packet_action in ACL_RULE table.', + 'eStr': self.defaultYANGFailure['InvalidValue'] + ['PACKET_ACTION'] + }, + 'ACL_TABLE_UNDEFINED_TABLE_TYPE': { + 'desc': 'Configure undefined acl_table_type in ACL_TABLE table.', + 'eStr': self.defaultYANGFailure['InvalidValue'] + ['type'] + }, + 'ACL_RULE_WITH_NON_EXIST_ACL_TABLE': { + 'desc': 'Configure non-existing ACL_TABLE in ACL_RULE.', + 'eStr': self.defaultYANGFailure['LeafRef'] + }, + 'ACL_RULE_IP_TYPE_SRC_IPV6_MISMATCH': { + 'desc': 'Configure IP_TYPE as ipv4any and SRC_IPV6 in ACL_RULE.', + 'eStr': self.defaultYANGFailure['When'] + ['IP_TYPE'] + }, + 'ACL_RULE_ARP_TYPE_DST_IPV6_MISMATCH': { + 'desc': 'Configure IP_TYPE as ARP and DST_IPV6 in ACL_RULE.', + 'eStr': self.defaultYANGFailure['When'] + ['IP_TYPE'] + }, + 'ACL_RULE_WRONG_L4_SRC_PORT_RANGE': { + 'desc': 'Configure l4_src_port_range as 99999-99999 in ACL_RULE', + 'eStr': self.defaultYANGFailure['Pattern'] + }, + 'ACL_RULE_ARP_TYPE_ICMPV6_CODE_MISMATCH': { + 'desc': 'Configure IP_TYPE as ARP and ICMPV6_CODE in ACL_RULE.', + 'eStr': self.defaultYANGFailure['When'] + ['IP_TYPE'] + }, + 'ACL_RULE_WRONG_INNER_ETHER_TYPE': { + 'desc': 'Configure INNER_ETHER_TYPE as 0x080C in ACL_RULE.', + 'eStr': self.defaultYANGFailure['Pattern'] + } + } + self.tests = tests + if (self.tests == None): + self.tests = self.ExceptionTests.keys() self.yangDir = yangDir self.jsonFile = jsonFile self.testNum = 1 @@ -80,9 +151,9 @@ def run(self): self.loadYangModel(self.yangDir) ret = 0 for test in self.tests: - func = "yangTest" + test; - # Pass function name, needed to retrieve input Json Dictionary - ret = ret + getattr(self, func)(func) + test = test.strip() + if test in self.ExceptionTests: + self.runExceptionTest(test); except Exception as e: printExceptionDetails() raise e @@ -92,14 +163,14 @@ def run(self): Get the JSON input based on func name and return jsonInput """ - def readJsonInput(self, func): + def readJsonInput(self, test): try: # load test specific Dictionary, using Key = func # this is to avoid loading very large JSON in memory - log.debug(" Read JSON Section: " + func) + log.debug(" Read JSON Section: " + test) jInput = "" with open(self.jsonFile, 'rb') as f: - jInst = ijson.items(f, func) + jInst = ijson.items(f, test) for it in jInst: jInput = jInput + json.dumps(it) log.debug(jInput) @@ -110,9 +181,9 @@ def readJsonInput(self, func): """ Log the start of a test """ - def logStartTest(self, tStr): + def logStartTest(self, desc): log.info("\n------------------- Test "+ str(self.testNum) +\ - ": " + tStr + "---------------------") + ": " + desc + "---------------------") self.testNum = self.testNum + 1 return @@ -130,256 +201,23 @@ def loadConfigData(self, jInput): return s """ - Test 1: Configure Wrong family with ip-prefix for VLAN_Interface Table. - """ - def yangTest1(self, func): - try: - tStr = " Configure Wrong family with ip-prefix for VLAN_Interface Table." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Must condition" in s and "not satisfied" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Test 2: Add dhcp_server which is not in correct ip-prefix format. - """ - def yangTest2(self, func): - try: - tStr = " Add dhcp_server which is not in correct ip-prefix format." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Invalid value" in s and "dhcp_servers" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure a member port in VLAN_MEMBER table which does not exist. - """ - def yangTest3(self, func): - try: - tStr = " Configure a member port in VLAN_MEMBER table which does not exist." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Leafref" in s and "non-existing" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure vlan-id in VLAN_MEMBER table which does not exist in VLAN table. - """ - def yangTest4(self, func): - try: - tStr = " Configure vlan-id in VLAN_MEMBER table which does not exist in VLAN table." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Leafref" in s and "non-existing" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure wrong value for tagging_mode. - """ - def yangTest5(self, func): - try: - tStr = " Configure wrong value for tagging_mode." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Invalid value" in s and "tagging_mode" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure empty string as ip-prefix in INTERFACE table. - """ - def yangTest6(self, func): - try: - tStr = " Configure empty string as ip-prefix in INTERFACE table." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Invalid value" in s and "ip-prefix" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure undefined packet_action in ACL_RULE table. - """ - def yangTest7(self, func): - try: - tStr = " Configure undefined packet_action in ACL_RULE table." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Invalid value" in s and "PACKET_ACTION" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure undefined acl_table_type in ACL_TABLE table. - """ - def yangTest8(self, func): - try: - tStr = " Configure undefined acl_table_type in ACL_TABLE table." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("Invalid value" in s and "type" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure non-existing ACL_TABLE in ACL_RULE. + Run Exception Test """ - def yangTest9(self, func): + def runExceptionTest(self, test): try: - tStr = " Configure non-existing ACL_TABLE in ACL_RULE." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) + desc = self.ExceptionTests[test]['desc'] + self.logStartTest(desc) + jInput = self.readJsonInput(test) # load the data, expect a exception with must condition failure s = self.loadConfigData(jInput) - if ("Leafref" in s and "non-existing" in s): - log.info(tStr + " Passed\n") + eStr = self.ExceptionTests[test]['eStr'] + log.debug(eStr) + if (sum(1 for str in eStr if str not in s) == 0): + log.info(desc + " Passed\n") return PASS except Exception as e: printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure IP_TYPE as ipv4any and SRC_IPV6 in ACL_RULE. - """ - def yangTest10(self, func): - try: - tStr = " Configure IP_TYPE as ipv4any and SRC_IPV6 in ACL_RULE." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("When condition" in s and "not satisfied" in s and "IP_TYPE" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure IP_TYPE as ARP and DST_IPV6 in ACL_RULE. - """ - def yangTest11(self, func): - try: - tStr = " Configure IP_TYPE as ARP and DST_IPV6 in ACL_RULE." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("When condition" in s and "not satisfied" in s and "IP_TYPE" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure l4_src_port_range as 99999-99999 in ACL_RULE. - """ - def yangTest12(self, func): - try: - tStr = " Configure l4_src_port_range as 99999-99999 in ACL_RULE." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("does not satisfy" in s and "pattern" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - """ - Configure IP_TYPE as ARP and ICMPV6_CODE in ACL_RULE. - """ - def yangTest13(self, func): - try: - tStr = " Configure IP_TYPE as ARP and ICMPV6_CODE in ACL_RULE." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("When condition" in s and "not satisfied" in s and "IP_TYPE" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") - return FAIL - - - """ - Configure INNER_ETHER_TYPE as 0x080C in ACL_RULE. - """ - def yangTest14(self, func): - try: - tStr = " Configure INNER_ETHER_TYPE as 0x080C in ACL_RULE." - self.logStartTest(tStr) - jInput = self.readJsonInput(func) - # load the data, expect a exception with must condition failure - s = self.loadConfigData(jInput) - if ("does not satisfy" in s and "pattern" in s): - log.info(tStr + " Passed\n") - return PASS - except Exception as e: - printExceptionDetails() - log.info(tStr + " Failed\n") + log.info(desc + " Failed\n") return FAIL # End of Class @@ -395,28 +233,39 @@ def main(): python yangModelTesting.py -h """) parser.add_argument('-t', '--tests', type=str, \ - help='Tests to run, Format 1,2,3', required=True) + help='tests to run separated by comma') parser.add_argument('-f', '--json-file', type=str, \ help='JSON input for tests ', required=True) parser.add_argument('-y', '--yang-dir', type=str, \ help='Path to YANG models', required=True) - parser.add_argument('-v', '--verbose-level', type=str, \ - help='Verbose Level of Logs', choices=['INFO', 'DEBUG']) + parser.add_argument('-v', '--verbose-level', \ + help='Verbose mode', action='store_true') + parser.add_argument('-l', '--list-tests', \ + help='list all tests', action='store_true') + args = parser.parse_args() try: tests = args.tests jsonFile = args.json_file yangDir = args.yang_dir logLevel = args.verbose_level - if logLevel == "DEBUG": + listTests = args.list_tests + if logLevel: log.setLevel(logging.DEBUG) # Make a list - tests = tests.split(",") + if (tests): + tests = tests.split(",") + yTest = YangModelTesting(tests, yangDir, jsonFile) + if (listTests): + log.info(yTest.ExceptionTests.keys()) + sys.exit(0) + ret = yTest.run() if ret == 0: log.info("All Test Passed") sys.exit(ret) + except Exception as e: printExceptionDetails() sys.exit(1) 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 e780ced82067..960a96e3aa91 100644 --- a/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json +++ b/src/sonic-yang-mgmt/tests/yang-model-tests/yangTest.json @@ -1,5 +1,5 @@ { - "yangTest1": { + "WRONG_FAMILY_WITH_IP_PREFIX": { "sonic-vlan:sonic-vlan": { "sonic-vlan:VLAN_INTERFACE": { "VLAN_INTERFACE_LIST": [{ @@ -18,7 +18,7 @@ } }, - "yangTest2": { + "DHCP_SERVER_INCORRECT_FORMAT": { "sonic-vlan:sonic-vlan": { "sonic-vlan:VLAN": { "VLAN_LIST": [{ @@ -34,7 +34,7 @@ } }, - "yangTest3": { + "VLAN_HAS_NON_EXIST_PORT": { "sonic-vlan:sonic-vlan": { "sonic-vlan:VLAN_MEMBER": { "VLAN_MEMBER_LIST": [{ @@ -73,7 +73,7 @@ } }, - "yangTest4": { + "VLAN_MEMEBER_WITH_NON_EXIST_VLAN": { "sonic-vlan:sonic-vlan": { "sonic-vlan:VLAN_MEMBER": { "VLAN_MEMBER_LIST": [{ @@ -108,7 +108,7 @@ } }, - "yangTest5": { + "TAGGING_MODE_WRONG_VALUE": { "sonic-vlan:sonic-vlan": { "sonic-vlan:VLAN_MEMBER": { "VLAN_MEMBER_LIST": [{ @@ -138,7 +138,7 @@ } }, - "yangTest6": { + "INTERFACE_IP_PREFIX_EMPTY_STRING": { "sonic-interface:sonic-interface": { "sonic-interface:INTERFACE": { "INTERFACE_LIST": [{ @@ -163,7 +163,7 @@ } }, - "yangTest7": { + "ACL_RULE_UNDEFINED_PACKET_ACTION": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{ @@ -209,7 +209,7 @@ } }, - "yangTest8": { + "ACL_TABLE_UNDEFINED_TABLE_TYPE": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_TABLE": { "ACL_TABLE_LIST": [{ @@ -244,7 +244,7 @@ } }, - "yangTest9": { + "ACL_RULE_WITH_NON_EXIST_ACL_TABLE": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{ @@ -290,7 +290,7 @@ } }, - "yangTest10": { + "ACL_RULE_IP_TYPE_SRC_IPV6_MISMATCH": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{ @@ -335,7 +335,7 @@ } }, - "yangTest11": { + "ACL_RULE_ARP_TYPE_DST_IPV6_MISMATCH": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{ @@ -380,7 +380,7 @@ } }, - "yangTest12": { + "ACL_RULE_WRONG_L4_SRC_PORT_RANGE": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{ @@ -427,7 +427,7 @@ } }, - "yangTest13": { + "ACL_RULE_ARP_TYPE_ICMPV6_CODE_MISMATCH": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{ @@ -475,7 +475,7 @@ } }, - "yangTest14": { + "ACL_RULE_WRONG_INNER_ETHER_TYPE": { "sonic-acl:sonic-acl": { "sonic-acl:ACL_RULE": { "ACL_RULE_LIST": [{