diff --git a/dump/main.py b/dump/main.py index c5878b2787..b936f18101 100644 --- a/dump/main.py +++ b/dump/main.py @@ -6,6 +6,7 @@ from tabulate import tabulate from sonic_py_common import multi_asic from utilities_common.constants import DEFAULT_NAMESPACE +from swsscommon.swsscommon import ConfigDBConnector from dump.match_infra import RedisSource, JsonSource, ConnectionPool from dump import plugins @@ -82,7 +83,10 @@ def state(ctx, module, identifier, db, table, key_map, verbose, namespace): params['namespace'] = namespace for arg in ids: params[plugins.dump_modules[module].ARG_NAME] = arg - collected_info[arg] = obj.execute(params) + try: + collected_info[arg] = obj.execute(params) + except ValueError as err: + click.fail(f"Failed to execute plugin: {err}") if len(db) > 0: collected_info = filter_out_dbs(db, collected_info) @@ -151,7 +155,7 @@ def populate_fv(info, module, namespace): db_cfg_file = JsonSource() db_conn = ConnectionPool().initialize_connector(namespace) for db_name in all_dbs: - if db_name is "CONFIG_FILE": + if db_name == "CONFIG_FILE": db_cfg_file.connect(plugins.dump_modules[module].CONFIG_FILE, namespace) else: db_conn.connect(db_name) @@ -164,7 +168,7 @@ def populate_fv(info, module, namespace): final_info[id][db_name]["keys"] = [] final_info[id][db_name]["tables_not_found"] = info[id][db_name]["tables_not_found"] for key in info[id][db_name]["keys"]: - if db_name is "CONFIG_FILE": + if db_name == "CONFIG_FILE": fv = db_cfg_file.get(db_name, key) else: fv = db_conn.get_all(db_name, key) @@ -174,9 +178,13 @@ def populate_fv(info, module, namespace): def get_dict_str(key_obj): + conn = ConfigDBConnector() table = [] - for pair in key_obj.items(): - table.append(list(pair)) + key_obj = conn.raw_to_typed(key_obj) + for field, value in key_obj.items(): + if isinstance(value, list): + value = "\n".join(value) + table.append((field, value)) return tabulate(table, headers=["field", "value"], tablefmt="psql") diff --git a/dump/match_helper.py b/dump/match_helper.py index aa0d7f9b19..a3899d47f4 100644 --- a/dump/match_helper.py +++ b/dump/match_helper.py @@ -44,7 +44,7 @@ def fetch_vlan_oid(match_engine, vlan_name, ns): vlan_num = int(vlan_name[4:]) # Find the table named "ASIC_STATE:SAI_OBJECT_TYPE_VLAN:*" in which SAI_VLAN_ATTR_VLAN_ID = vlan_num - req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_VLAN", key_pattern="*", field="SAI_VLAN_ATTR_VLAN_ID", + req = MatchRequest(db="ASIC_DB", table="ASIC_STATE:SAI_OBJECT_TYPE_VLAN", key_pattern="*", field="SAI_VLAN_ATTR_VLAN_ID", value=str(vlan_num), ns=ns) ret = match_engine.fetch(req) vlan_oid = "" @@ -100,3 +100,15 @@ def fetch_lag_oid(match_engine, lag_name, ns): lag_oids matched {}".format(lag_name, lag_type_oids), lag_type_oids[-1]) lag_type_oid = lag_type_oids[-1] return lag_type_oid + +# ACL helpers + +def fetch_acl_counter_oid(match_engine, acl_table_name, acl_rule_name, ns): + """ + Fetch ACL counter OID from COUNTERS DB for a particular rule + """ + counters_db = match_engine.get_redis_source_adapter() + counters_db.connect("COUNTERS_DB", ns) + counters = counters_db.hgetall("COUNTERS_DB", "ACL_COUNTER_RULE_MAP") + counter_oid = counters.get(f"{acl_table_name}{counters_db.get_separator('COUNTERS_DB')}{acl_rule_name}") + return counter_oid diff --git a/dump/match_infra.py b/dump/match_infra.py index a28c7f9bf8..ec493d2bcf 100644 --- a/dump/match_infra.py +++ b/dump/match_infra.py @@ -154,6 +154,10 @@ def hget(self, db, key, field): def get_separator(self, db): return "" + @abstractmethod + def hgetall(self, db, key): + raise NotImplementedError + class RedisSource(SourceAdapter): """ Concrete Adaptor Class for connecting to Redis Data Sources """ @@ -182,6 +186,9 @@ def get(self, db, key): def hget(self, db, key, field): return self.conn.get(db, key, field) + def hgetall(self, db, key): + return self.conn.get_all(db, key) + class JsonSource(SourceAdapter): """ Concrete Adaptor Class for connecting to JSON Data Sources """ @@ -219,6 +226,11 @@ def hget(self, db, key, field): table, key = key.split(sep, 1) return self.json_data.get(table, "").get(key, "").get(field, "") + def hgetall(self, db, key): + sep = self.get_separator(db) + table, key = key.split(sep, 1) + return self.json_data.get(table, {}).get(key) + class ConnectionPool: """ Caches SonicV2Connector objects for effective reuse """ @@ -271,15 +283,21 @@ def __init__(self, pool=None): def clear_cache(self, ns): self.conn_pool(ns) + def get_redis_source_adapter(self): + return RedisSource(self.conn_pool) + + def get_json_source_adapter(self): + return JsonSource() + def __get_source_adapter(self, req): src = None d_src = "" if req.db: d_src = req.db - src = RedisSource(self.conn_pool) + src = self.get_redis_source_adapter() else: d_src = req.file - src = JsonSource() + src = self.get_json_source_adapter() return d_src, src def __create_template(self): diff --git a/dump/plugins/acl_rule.py b/dump/plugins/acl_rule.py new file mode 100755 index 0000000000..406769b5ea --- /dev/null +++ b/dump/plugins/acl_rule.py @@ -0,0 +1,84 @@ +from dump.helper import create_template_dict +from dump.match_infra import MatchRequest +from swsscommon.swsscommon import SonicDBConfig + +from dump.match_helper import fetch_acl_counter_oid +from .executor import Executor + + +CFG_DB_SEPARATOR = SonicDBConfig.getSeparator("CONFIG_DB") +ASIC_DB_SEPARATOR = SonicDBConfig.getSeparator("ASIC_DB") + + +class Acl_Rule(Executor): + """ + Debug Dump Plugin for ACL Rule Module + """ + ARG_NAME = "acl_rule_name" + + def __init__(self, match_engine=None): + super().__init__(match_engine) + + def get_all_args(self, ns=""): + req = MatchRequest(db="CONFIG_DB", table="ACL_RULE", key_pattern="*", ns=ns) + ret = self.match_engine.fetch(req) + acl_rules = ret["keys"] + return [key.split(CFG_DB_SEPARATOR, 1)[-1] for key in acl_rules] + + def execute(self, params): + self.ret_temp = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + + try: + acl_table_name, acl_rule_name = params[self.ARG_NAME].split(CFG_DB_SEPARATOR, 1) + except ValueError: + raise ValueError(f"Invalid rule name passed {params[self.ARG_NAME]}") + + self.ns = params["namespace"] + self.init_acl_rule_config_info(acl_table_name, acl_rule_name) + self.init_acl_rule_asic_info(acl_table_name, acl_rule_name) + return self.ret_temp + + def init_acl_rule_config_info(self, acl_table_name, acl_rule_name): + req = MatchRequest(db="CONFIG_DB", table="ACL_RULE", + key_pattern=CFG_DB_SEPARATOR.join([acl_table_name, acl_rule_name]), ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + def init_acl_rule_asic_info(self, acl_table_name, acl_rule_name): + counter_oid = fetch_acl_counter_oid(self.match_engine, acl_table_name, acl_rule_name, self.ns) + if not counter_oid: + return + + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_COUNTER"]), + key_pattern=counter_oid, return_fields=["SAI_ACL_COUNTER_ATTR_TABLE_ID"], ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + return_values = ret["return_values"] + counter_object = return_values.get(ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_COUNTER", counter_oid]), {}) + table_oid = counter_object.get("SAI_ACL_COUNTER_ATTR_TABLE_ID") + if not table_oid: + raise Exception("Invalid counter object without table OID in ASIC_DB") + + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_ENTRY"]), key_pattern="*", + field="SAI_ACL_ENTRY_ATTR_TABLE_ID", value=table_oid, + return_fields=["SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE"], ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + range_oids = set() + for _, entry in ret["return_values"].items(): + range_attr_value = entry.get("SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE") + if not range_attr_value: + continue + ranges_attr_value = range_attr_value.split(ASIC_DB_SEPARATOR, 1) + if len(range_attr_value) < 2: + raise Exception("Invalid SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE field format") + for oid in ranges_attr_value[1].split(','): + range_oids.add(oid) + + for range_oid in range_oids: + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_RANGE"]), + key_pattern=range_oid, ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) diff --git a/dump/plugins/acl_table.py b/dump/plugins/acl_table.py new file mode 100755 index 0000000000..4ae023dd5d --- /dev/null +++ b/dump/plugins/acl_table.py @@ -0,0 +1,91 @@ +from dump.helper import create_template_dict +from dump.match_infra import MatchRequest +from swsscommon.swsscommon import SonicDBConfig + +from dump.match_helper import fetch_acl_counter_oid +from .executor import Executor + + +CFG_DB_SEPARATOR = SonicDBConfig.getSeparator("CONFIG_DB") +ASIC_DB_SEPARATOR = SonicDBConfig.getSeparator("ASIC_DB") + + +class Acl_Table(Executor): + """ + Debug Dump Plugin for ACL Table Module + """ + ARG_NAME = "acl_table_name" + + def __init__(self, match_engine=None): + super().__init__(match_engine) + + def get_all_args(self, ns=""): + req = MatchRequest(db="CONFIG_DB", table="ACL_TABLE", key_pattern="*", ns=ns) + ret = self.match_engine.fetch(req) + acl_tables = ret["keys"] + return [key.split(CFG_DB_SEPARATOR)[-1] for key in acl_tables] + + def execute(self, params): + self.ret_temp = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + acl_table_name = params[self.ARG_NAME] + self.ns = params["namespace"] + self.init_acl_table_config_info(acl_table_name) + self.init_acl_table_asic_info(acl_table_name) + return self.ret_temp + + def init_acl_table_config_info(self, acl_table_name): + req = MatchRequest(db="CONFIG_DB", table="ACL_TABLE", key_pattern=acl_table_name, return_fields=["type"], ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + # Find corresponding ACL table type in CONFIG DB + return_values = ret["return_values"] + acl_table_type_name = return_values.get(CFG_DB_SEPARATOR.join(["ACL_TABLE", acl_table_name]), {}).get("type") + req = MatchRequest(db="CONFIG_DB", table="ACL_TABLE_TYPE", key_pattern=acl_table_type_name, ns=self.ns) + ret = self.match_engine.fetch(req) + # If not found don't add it to the table, it might be a default table type + if ret["keys"]: + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + def init_acl_table_asic_info(self, acl_table_name): + req = MatchRequest(db="CONFIG_DB", table="ACL_RULE", key_pattern=CFG_DB_SEPARATOR.join([acl_table_name, "*"]), ns=self.ns) + ret = self.match_engine.fetch(req) + acl_rules = ret["keys"] + if not acl_rules: + return + acl_rule_name = acl_rules[0].split(CFG_DB_SEPARATOR)[-1] + + counter_oid = fetch_acl_counter_oid(self.match_engine, acl_table_name, acl_rule_name, self.ns) + if not counter_oid: + return + + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_COUNTER"]), + key_pattern=counter_oid, return_fields=["SAI_ACL_COUNTER_ATTR_TABLE_ID"], ns=self.ns) + ret = self.match_engine.fetch(req) + + return_values = ret["return_values"] + counter_object = return_values.get(ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_COUNTER", counter_oid]), {}) + table_oid = counter_object.get("SAI_ACL_COUNTER_ATTR_TABLE_ID") + if not table_oid: + raise Exception("Invalid counter object without table OID in ASIC_DB") + + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_TABLE"]), + key_pattern=table_oid, ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER"]), + key_pattern="*", field="SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID", + value=table_oid, return_fields=["SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID"], ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) + + group_oids = set() + for _, entry in ret["return_values"].items(): + group_oids.add(entry.get("SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID")) + + for group_oid in group_oids: + req = MatchRequest(db="ASIC_DB", table=ASIC_DB_SEPARATOR.join(["ASIC_STATE", "SAI_OBJECT_TYPE_ACL_TABLE_GROUP"]), + key_pattern=group_oid, ns=self.ns) + ret = self.match_engine.fetch(req) + self.add_to_ret_template(req.table, req.db, ret["keys"], ret["error"]) diff --git a/tests/dump_input/acl/asic_db.json b/tests/dump_input/acl/asic_db.json new file mode 100755 index 0000000000..727582336a --- /dev/null +++ b/tests/dump_input/acl/asic_db.json @@ -0,0 +1,75 @@ +{ + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE:oid:0x7000000000600": { + "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST": "2:SAI_ACL_BIND_POINT_TYPE_PORT,SAI_ACL_BIND_POINT_TYPE_LAG", + "SAI_ACL_TABLE_ATTR_ACL_STAGE": "SAI_ACL_STAGE_INGRESS", + "SAI_ACL_TABLE_ATTR_FIELD_ACL_ETHER_TYPE": "true" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc000000000601": { + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": "oid:0xb0000000005f5", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": "oid:0x7000000000600", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": "100" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc000000000602": { + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": "oid:0xb0000000005f7", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": "oid:0x7000000000600", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": "100" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP:oid:0xb0000000005f5": { + "SAI_ACL_TABLE_GROUP_ATTR_ACL_BIND_POINT_TYPE_LIST": "1:SAI_ACL_BIND_POINT_TYPE_PORT", + "SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE": "SAI_ACL_STAGE_INGRESS", + "SAI_ACL_TABLE_GROUP_ATTR_TYPE": "SAI_ACL_TABLE_GROUP_TYPE_PARALLEL" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP:oid:0xb0000000005f7": { + "SAI_ACL_TABLE_GROUP_ATTR_ACL_BIND_POINT_TYPE_LIST": "1:SAI_ACL_BIND_POINT_TYPE_PORT", + "SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE": "SAI_ACL_STAGE_INGRESS", + "SAI_ACL_TABLE_GROUP_ATTR_TYPE": "SAI_ACL_TABLE_GROUP_TYPE_PARALLEL" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY:oid:0x8000000000609": { + "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": "oid:0x9000000000606", + "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": "SAI_PACKET_ACTION_FORWARD", + "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": "true", + "SAI_ACL_ENTRY_ATTR_PRIORITY": "9995", + "SAI_ACL_ENTRY_ATTR_TABLE_ID": "oid:0x7000000000600" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_COUNTER:oid:0x9000000000606": { + "SAI_ACL_COUNTER_ATTR_TABLE_ID": "oid:0x7000000000600", + "SAI_ACL_COUNTER_ATTR_ENABLE_PACKET_COUNT": "true", + "SAI_ACL_COUNTER_ATTR_ENABLE_BYTES_COUNT": "true" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE:oid:0x7100000000600": { + "SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST": "2:SAI_ACL_BIND_POINT_TYPE_PORT,SAI_ACL_BIND_POINT_TYPE_LAG", + "SAI_ACL_TABLE_ATTR_ACL_STAGE": "SAI_ACL_STAGE_INGRESS", + "SAI_ACL_TABLE_ATTR_FIELD_ACL_ETHER_TYPE": "true" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc100000000601": { + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": "oid:0xb0000000005f5", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": "oid:0x7100000000600", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": "100" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc100000000602": { + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": "oid:0xb0000000005f7", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": "oid:0x7100000000600", + "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": "100" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY:oid:0x8100000000609": { + "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": "oid:0x9100000000606", + "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": "SAI_PACKET_ACTION_FORWARD", + "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": "true", + "SAI_ACL_ENTRY_ATTR_PRIORITY": "9995", + "SAI_ACL_ENTRY_ATTR_TABLE_ID": "oid:0x7100000000600", + "SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE": "2:oid:0xa100000000607,oid:0xa100000000608" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_COUNTER:oid:0x9100000000606": { + "SAI_ACL_COUNTER_ATTR_TABLE_ID": "oid:0x7100000000600", + "SAI_ACL_COUNTER_ATTR_ENABLE_PACKET_COUNT": "true", + "SAI_ACL_COUNTER_ATTR_ENABLE_BYTES_COUNT": "true" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE:oid:0xa100000000607": { + "SAI_ACL_RANGE_ATTR_LIMIT": "90,95", + "SAI_ACL_RANGE_ATTR_TYPE": "SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE" + }, + "ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE:oid:0xa100000000608": { + "SAI_ACL_RANGE_ATTR_LIMIT": "90,95", + "SAI_ACL_RANGE_ATTR_TYPE": "SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE" + } +} diff --git a/tests/dump_input/acl/config_db.json b/tests/dump_input/acl/config_db.json new file mode 100755 index 0000000000..faa2fd8470 --- /dev/null +++ b/tests/dump_input/acl/config_db.json @@ -0,0 +1,43 @@ +{ + "ACL_TABLE|SNMP_ACL": { + "policy_desc": "SNMP_ACL", + "services": [ + "SNMP" + ], + "stage": "ingress", + "type": "CTRLPLANE" + }, + "ACL_TABLE_TYPE|MY_TYPE": { + "matches": "ETHER_TYPE,L4_DST_PORT_RANGE,L4_SRC_PORT_RANGE ", + "bind_point_types": "port" + }, + "ACL_TABLE|DATAACL": { + "policy_desc": "Some ACL table", + "ports": "Ethernet0,Ethernet4", + "stage": "ingress", + "type": "L3" + }, + "ACL_TABLE|DATAACL1": { + "policy_desc": "Some ACL table", + "ports": "Ethernet0,Ethernet4", + "stage": "ingress", + "type": "L3" + }, + "ACL_TABLE|DATAACL2": { + "policy_desc": "Some ACL table", + "ports": "Ethernet0,Ethernet4", + "stage": "ingress", + "type": "MY_TYPE" + }, + "ACL_RULE|DATAACL|R0": { + "ETHER_TYPE": "2048", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "999" + }, + "ACL_RULE|DATAACL2|R0": { + "L4_SRC_PORT_RANGE ": "90-95", + "L4_DST_PORT_RANGE ": "90-95", + "PACKET_ACTION": "FORWARD", + "PRIORITY": "999" + } +} diff --git a/tests/dump_input/acl/counters_db.json b/tests/dump_input/acl/counters_db.json new file mode 100755 index 0000000000..ea9d71a886 --- /dev/null +++ b/tests/dump_input/acl/counters_db.json @@ -0,0 +1,6 @@ +{ + "ACL_COUNTER_RULE_MAP": { + "DATAACL:R0": "oid:0x9000000000606", + "DATAACL2:R0": "oid:0x9100000000606" + } +} diff --git a/tests/dump_tests/module_tests/acl_test.py b/tests/dump_tests/module_tests/acl_test.py new file mode 100755 index 0000000000..67698b034a --- /dev/null +++ b/tests/dump_tests/module_tests/acl_test.py @@ -0,0 +1,130 @@ +import os +import pytest +from deepdiff import DeepDiff +from dump.helper import create_template_dict, sort_lists, populate_mock +from dump.plugins.acl_table import Acl_Table +from dump.plugins.acl_rule import Acl_Rule +from dump.match_infra import MatchEngine, ConnectionPool +from swsscommon.swsscommon import SonicV2Connector + +# Location for dedicated db's used for UT +module_tests_path = os.path.dirname(__file__) +dump_tests_path = os.path.join(module_tests_path, "../") +tests_path = os.path.join(dump_tests_path, "../") +dump_test_input = os.path.join(tests_path, "dump_input") +port_files_path = os.path.join(dump_test_input, "acl") + +# Define the mock files to read from +dedicated_dbs = {} +dedicated_dbs['CONFIG_DB'] = os.path.join(port_files_path, "config_db.json") +dedicated_dbs['COUNTERS_DB'] = os.path.join(port_files_path, "counters_db.json") +dedicated_dbs['ASIC_DB'] = os.path.join(port_files_path, "asic_db.json") + + +@pytest.fixture(scope="class", autouse=True) +def match_engine(): + os.environ["VERBOSE"] = "1" + + # Monkey Patch the SonicV2Connector Object + from ...mock_tables import dbconnector + db = SonicV2Connector() + + # popualate the db with mock data + db_names = list(dedicated_dbs.keys()) + try: + populate_mock(db, db_names, dedicated_dbs) + except Exception as e: + assert False, "Mock initialization failed: " + str(e) + + # Initialize connection pool + conn_pool = ConnectionPool() + DEF_NS = '' # Default Namespace + conn_pool.cache = {DEF_NS: {'conn': db, + 'connected_to': set(db_names)}} + + # Initialize match_engine + match_engine = MatchEngine(conn_pool) + yield match_engine + os.environ["VERBOSE"] = "0" + + +@pytest.mark.usefixtures("match_engine") +class TestAclTableModule: + def test_basic(self, match_engine): + """ + Scenario: When the basic config is properly applied and propagated + """ + params = {Acl_Table.ARG_NAME: "DATAACL", "namespace": ""} + m_acl_table = Acl_Table(match_engine) + returned = m_acl_table.execute(params) + expect = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + expect["CONFIG_DB"]["keys"].append("ACL_TABLE|DATAACL") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE:oid:0x7000000000600") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc000000000601") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc000000000602") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP:oid:0xb0000000005f5") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP:oid:0xb0000000005f7") + ddiff = DeepDiff(sort_lists(returned), sort_lists(expect)) + assert not ddiff, ddiff + + def test_no_counter_mapping(self, match_engine): + """ + Scenario: When there is no ACL_COUNTER_RULE_MAP mapping for rule + """ + params = {Acl_Table.ARG_NAME: "DATAACL1", "namespace": ""} + m_acl_table = Acl_Table(match_engine) + returned = m_acl_table.execute(params) + expect = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + expect["CONFIG_DB"]["keys"].append("ACL_TABLE|DATAACL1") + ddiff = DeepDiff(sort_lists(returned), sort_lists(expect)) + assert not ddiff, ddiff + + def test_with_table_type(self, match_engine): + """ + Scenario: When there is ACL_TABLE_TYPE configured for this table + """ + params = {Acl_Table.ARG_NAME: "DATAACL2", "namespace": ""} + m_acl_table = Acl_Table(match_engine) + returned = m_acl_table.execute(params) + expect = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + expect["CONFIG_DB"]["keys"].append("ACL_TABLE|DATAACL2") + expect["CONFIG_DB"]["keys"].append("ACL_TABLE_TYPE|MY_TYPE") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE:oid:0x7100000000600") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc100000000601") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER:oid:0xc100000000602") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP:oid:0xb0000000005f5") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP:oid:0xb0000000005f7") + ddiff = DeepDiff(sort_lists(returned), sort_lists(expect)) + assert not ddiff, ddiff + +@pytest.mark.usefixtures("match_engine") +class TestAclRuleModule: + def test_basic(self, match_engine): + """ + Scenario: When the config is properly applied and propagated + """ + params = {Acl_Rule.ARG_NAME: "DATAACL|R0", "namespace": ""} + m_acl_rule = Acl_Rule(match_engine) + returned = m_acl_rule.execute(params) + expect = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + expect["CONFIG_DB"]["keys"].append("ACL_RULE|DATAACL|R0") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_COUNTER:oid:0x9000000000606") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY:oid:0x8000000000609") + ddiff = DeepDiff(sort_lists(returned), sort_lists(expect)) + assert not ddiff, ddiff + + def test_with_ranges(self, match_engine): + """ + Scenario: When ACL rule has range configuration + """ + params = {Acl_Rule.ARG_NAME: "DATAACL2|R0", "namespace": ""} + m_acl_rule = Acl_Rule(match_engine) + returned = m_acl_rule.execute(params) + expect = create_template_dict(dbs=["CONFIG_DB", "ASIC_DB"]) + expect["CONFIG_DB"]["keys"].append("ACL_RULE|DATAACL2|R0") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_COUNTER:oid:0x9100000000606") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY:oid:0x8100000000609") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE:oid:0xa100000000607") + expect["ASIC_DB"]["keys"].append("ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE:oid:0xa100000000608") + ddiff = DeepDiff(sort_lists(returned), sort_lists(expect)) + assert not ddiff, ddiff