Skip to content

Commit

Permalink
[dump] implement ACL modules (#2153)
Browse files Browse the repository at this point in the history
- What I did
Implemented ACL dump modules

- How I did it
Added dump plugins for acl_rule and acl_table.
Added UT for coverage.
Implemented according to sonic-net/SONiC#983

- How to verify it
Run UT, run on the switch mannually.

Signed-off-by: Stepan Blyschak <stepanb@nvidia.com>
  • Loading branch information
stepanblyschak authored May 23, 2022
1 parent 494dd62 commit 2f53bd4
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 8 deletions.
18 changes: 13 additions & 5 deletions dump/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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")


Expand Down
14 changes: 13 additions & 1 deletion dump/match_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ""
Expand Down Expand Up @@ -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
22 changes: 20 additions & 2 deletions dump/match_infra.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down Expand Up @@ -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 """
Expand Down Expand Up @@ -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):
Expand Down
84 changes: 84 additions & 0 deletions dump/plugins/acl_rule.py
Original file line number Diff line number Diff line change
@@ -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"])
91 changes: 91 additions & 0 deletions dump/plugins/acl_table.py
Original file line number Diff line number Diff line change
@@ -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"])
75 changes: 75 additions & 0 deletions tests/dump_input/acl/asic_db.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
Loading

0 comments on commit 2f53bd4

Please sign in to comment.