Skip to content

Commit f88ca1c

Browse files
bingwang-msyxieca
authored andcommitted
Improve show acl commands (#2667)
* Add status for ACL_TABLE and ACL_RULE in STATE_DB
1 parent 738406b commit f88ca1c

File tree

9 files changed

+213
-15
lines changed

9 files changed

+213
-15
lines changed

acl_loader/main.py

+62-13
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ class AclLoader(object):
7272

7373
ACL_TABLE = "ACL_TABLE"
7474
ACL_RULE = "ACL_RULE"
75+
CFG_ACL_TABLE = "ACL_TABLE"
76+
STATE_ACL_TABLE = "ACL_TABLE_TABLE"
77+
CFG_ACL_RULE = "ACL_RULE"
78+
STATE_ACL_RULE = "ACL_RULE_TABLE"
7579
ACL_TABLE_TYPE_MIRROR = "MIRROR"
7680
ACL_TABLE_TYPE_CTRLPLANE = "CTRLPLANE"
7781
CFG_MIRROR_SESSION_TABLE = "MIRROR_SESSION"
@@ -117,11 +121,16 @@ def __init__(self):
117121
self.tables_db_info = {}
118122
self.rules_db_info = {}
119123
self.rules_info = {}
124+
self.tables_state_info = None
125+
self.rules_state_info = None
120126

121127
# Load database config files
122128
load_db_config()
123129

124130
self.sessions_db_info = {}
131+
self.acl_table_status = {}
132+
self.acl_rule_status = {}
133+
125134
self.configdb = ConfigDBConnector()
126135
self.configdb.connect()
127136
self.statedb = SonicV2Connector(host="127.0.0.1")
@@ -156,6 +165,8 @@ def __init__(self):
156165
self.read_rules_info()
157166
self.read_sessions_info()
158167
self.read_policers_info()
168+
self.acl_table_status = self.read_acl_object_status_info(self.CFG_ACL_TABLE, self.STATE_ACL_TABLE)
169+
self.acl_rule_status = self.read_acl_object_status_info(self.CFG_ACL_RULE, self.STATE_ACL_RULE)
159170

160171
def read_tables_info(self):
161172
"""
@@ -210,7 +221,7 @@ def read_sessions_info(self):
210221
for key in self.sessions_db_info:
211222
if self.per_npu_statedb:
212223
# For multi-npu platforms we will read from all front asic name space
213-
# statedb as the monitor port will be differnt for each asic
224+
# statedb as the monitor port will be different for each asic
214225
# and it's status also might be different (ideally should not happen)
215226
# We will store them as dict of 'asic' : value
216227
self.sessions_db_info[key]["status"] = {}
@@ -224,6 +235,35 @@ def read_sessions_info(self):
224235
self.sessions_db_info[key]["status"] = state_db_info.get("status", "inactive") if state_db_info else "error"
225236
self.sessions_db_info[key]["monitor_port"] = state_db_info.get("monitor_port", "") if state_db_info else ""
226237

238+
def read_acl_object_status_info(self, cfg_db_table_name, state_db_table_name):
239+
"""
240+
Read ACL_TABLE status or ACL_RULE status from STATE_DB
241+
"""
242+
if self.per_npu_configdb:
243+
namespace_configdb = list(self.per_npu_configdb.values())[0]
244+
keys = namespace_configdb.get_table(cfg_db_table_name).keys()
245+
else:
246+
keys = self.configdb.get_table(cfg_db_table_name).keys()
247+
248+
status = {}
249+
for key in keys:
250+
# For ACL_RULE, the key is (acl_table_name, acl_rule_name)
251+
if isinstance(key, tuple):
252+
state_db_key = key[0] + "|" + key[1]
253+
else:
254+
state_db_key = key
255+
status[key] = {}
256+
if self.per_npu_statedb:
257+
status[key]['status'] = {}
258+
for namespace_key, namespace_statedb in self.per_npu_statedb.items():
259+
state_db_info = namespace_statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(state_db_table_name, state_db_key))
260+
status[key]['status'][namespace_key] = state_db_info.get("status", "N/A") if state_db_info else "N/A"
261+
else:
262+
state_db_info = self.statedb.get_all(self.statedb.STATE_DB, "{}|{}".format(state_db_table_name, state_db_key))
263+
status[key]['status'] = state_db_info.get("status", "N/A") if state_db_info else "N/A"
264+
265+
return status
266+
227267
def get_sessions_db_info(self):
228268
return self.sessions_db_info
229269

@@ -786,32 +826,36 @@ def show_table(self, table_name):
786826
:param table_name: Optional. ACL table name. Filter tables by specified name.
787827
:return:
788828
"""
789-
header = ("Name", "Type", "Binding", "Description", "Stage")
829+
header = ("Name", "Type", "Binding", "Description", "Stage", "Status")
790830

791831
data = []
792832
for key, val in self.get_tables_db_info().items():
793833
if table_name and key != table_name:
794834
continue
795-
835+
796836
stage = val.get("stage", Stage.INGRESS).lower()
797-
837+
# Get ACL table status from STATE_DB
838+
if key in self.acl_table_status:
839+
status = self.acl_table_status[key]['status']
840+
else:
841+
status = 'N/A'
798842
if val["type"] == AclLoader.ACL_TABLE_TYPE_CTRLPLANE:
799843
services = natsorted(val["services"])
800-
data.append([key, val["type"], services[0], val["policy_desc"], stage])
844+
data.append([key, val["type"], services[0], val["policy_desc"], stage, status])
801845

802846
if len(services) > 1:
803847
for service in services[1:]:
804-
data.append(["", "", service, "", ""])
848+
data.append(["", "", service, "", "", ""])
805849
else:
806850
if not val["ports"]:
807-
data.append([key, val["type"], "", val["policy_desc"], stage])
851+
data.append([key, val["type"], "", val["policy_desc"], stage, status])
808852
else:
809853
ports = natsorted(val["ports"])
810-
data.append([key, val["type"], ports[0], val["policy_desc"], stage])
854+
data.append([key, val["type"], ports[0], val["policy_desc"], stage, status])
811855

812856
if len(ports) > 1:
813857
for port in ports[1:]:
814-
data.append(["", "", port, "", ""])
858+
data.append(["", "", port, "", "", ""])
815859

816860
print(tabulate.tabulate(data, headers=header, tablefmt="simple", missingval=""))
817861

@@ -871,7 +915,7 @@ def show_rule(self, table_name, rule_id):
871915
:param rule_id: Optional. ACL rule name. Filter rule by specified rule name.
872916
:return:
873917
"""
874-
header = ("Table", "Rule", "Priority", "Action", "Match")
918+
header = ("Table", "Rule", "Priority", "Action", "Match", "Status")
875919

876920
def pop_priority(val):
877921
priority = "N/A"
@@ -917,11 +961,16 @@ def pop_matches(val):
917961
priority = pop_priority(val)
918962
action = pop_action(val)
919963
matches = pop_matches(val)
920-
921-
rule_data = [[tname, rid, priority, action, matches[0]]]
964+
# Get ACL rule status from STATE_DB
965+
status_key = (tname, rid)
966+
if status_key in self.acl_rule_status:
967+
status = self.acl_rule_status[status_key]['status']
968+
else:
969+
status = "N/A"
970+
rule_data = [[tname, rid, priority, action, matches[0], status]]
922971
if len(matches) > 1:
923972
for m in matches[1:]:
924-
rule_data.append(["", "", "", "", m])
973+
rule_data.append(["", "", "", "", m, ""])
925974

926975
raw_data.append([priority, rule_data])
927976

tests/aclshow_test.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
RULE_9 DATAACL 9991 901 900
4747
RULE_10 DATAACL 9989 1001 1000
4848
DEFAULT_RULE DATAACL 1 2 1
49+
RULE_1 DATAACL_5 9999 N/A N/A
4950
RULE_NO_COUNTER DATAACL_NO_COUNTER 9995 N/A N/A
5051
RULE_6 EVERFLOW 9994 601 600
5152
RULE_08 EVERFLOW 9992 0 0
@@ -89,8 +90,8 @@
8990
# Expected output for aclshow -r RULE_4,RULE_6 -vv
9091
rule4_rule6_verbose_output = '' + \
9192
"""Reading ACL info...
92-
Total number of ACL Tables: 11
93-
Total number of ACL Rules: 20
93+
Total number of ACL Tables: 12
94+
Total number of ACL Rules: 21
9495
9596
RULE NAME TABLE NAME PRIO PACKETS COUNT BYTES COUNT
9697
----------- ------------ ------ --------------- -------------
@@ -136,6 +137,7 @@
136137
RULE_9 DATAACL 9991 0 0
137138
RULE_10 DATAACL 9989 0 0
138139
DEFAULT_RULE DATAACL 1 0 0
140+
RULE_1 DATAACL_5 9999 N/A N/A
139141
RULE_NO_COUNTER DATAACL_NO_COUNTER 9995 N/A N/A
140142
RULE_6 EVERFLOW 9994 0 0
141143
RULE_08 EVERFLOW 9992 0 0
@@ -161,6 +163,7 @@
161163
RULE_9 DATAACL 9991 0 0
162164
RULE_10 DATAACL 9989 0 0
163165
DEFAULT_RULE DATAACL 1 0 0
166+
RULE_1 DATAACL_5 9999 N/A N/A
164167
RULE_NO_COUNTER DATAACL_NO_COUNTER 9995 100 100
165168
RULE_6 EVERFLOW 9994 0 0
166169
RULE_08 EVERFLOW 9992 0 0

tests/mock_tables/asic0/config_db.json

+11
Original file line numberDiff line numberDiff line change
@@ -246,5 +246,16 @@
246246
"holdtime": "10",
247247
"asn": "65200",
248248
"keepalive": "3"
249+
},
250+
"ACL_RULE|DATAACL_5|RULE_1": {
251+
"IP_PROTOCOL": "126",
252+
"PACKET_ACTION": "FORWARD",
253+
"PRIORITY": "9999"
254+
},
255+
"ACL_TABLE|DATAACL_5": {
256+
"policy_desc": "DATAACL_5",
257+
"ports@": "Ethernet124",
258+
"type": "L3",
259+
"stage": "ingress"
249260
}
250261
}

tests/mock_tables/asic0/state_db.json

+6
Original file line numberDiff line numberDiff line change
@@ -249,5 +249,11 @@
249249
"STATUS": "up",
250250
"REMOTE_MOD": "0",
251251
"REMOTE_PORT": "93"
252+
},
253+
"ACL_TABLE_TABLE|DATAACL_5" : {
254+
"status": "Active"
255+
},
256+
"ACL_RULE_TABLE|DATAACL_5|RULE_1" : {
257+
"status": "Active"
252258
}
253259
}

tests/mock_tables/asic2/config_db.json

+11
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,16 @@
124124
"state": "disabled",
125125
"auto_restart": "disabled",
126126
"high_mem_alert": "disabled"
127+
},
128+
"ACL_RULE|DATAACL_5|RULE_1": {
129+
"IP_PROTOCOL": "126",
130+
"PACKET_ACTION": "FORWARD",
131+
"PRIORITY": "9999"
132+
},
133+
"ACL_TABLE|DATAACL_5": {
134+
"policy_desc": "DATAACL_5",
135+
"ports@": "Ethernet124",
136+
"type": "L3",
137+
"stage": "ingress"
127138
}
128139
}

tests/mock_tables/asic2/state_db.json

+6
Original file line numberDiff line numberDiff line change
@@ -207,5 +207,11 @@
207207
"speed_target": "50",
208208
"led_status": "green",
209209
"timestamp": "20200813 01:32:30"
210+
},
211+
"ACL_TABLE_TABLE|DATAACL_5" : {
212+
"status": "Active"
213+
},
214+
"ACL_RULE_TABLE|DATAACL_5|RULE_1" : {
215+
"status": "Active"
210216
}
211217
}

tests/mock_tables/config_db.json

+11
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,11 @@
496496
"PACKET_ACTION": "FORWARD",
497497
"PRIORITY": "9995"
498498
},
499+
"ACL_RULE|DATAACL_5|RULE_1": {
500+
"IP_PROTOCOL": "126",
501+
"PACKET_ACTION": "FORWARD",
502+
"PRIORITY": "9999"
503+
},
499504
"ACL_TABLE|NULL_ROUTE_V4": {
500505
"policy_desc": "DATAACL",
501506
"ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023",
@@ -533,6 +538,12 @@
533538
"type": "L3V6",
534539
"stage": "egress"
535540
},
541+
"ACL_TABLE|DATAACL_5": {
542+
"policy_desc": "DATAACL_5",
543+
"ports@": "Ethernet124",
544+
"type": "L3",
545+
"stage": "ingress"
546+
},
536547
"ACL_TABLE|EVERFLOW": {
537548
"policy_desc": "EVERFLOW",
538549
"ports@": "PortChannel0002,PortChannel0005,PortChannel0008,PortChannel0011,PortChannel0014,PortChannel0017,PortChannel0020,PortChannel0023,Ethernet100,Ethernet104,Ethernet92,Ethernet96,Ethernet84,Ethernet88,Ethernet76,Ethernet80,Ethernet108,Ethernet112,Ethernet64,Ethernet120,Ethernet116,Ethernet124,Ethernet72,Ethernet68",

tests/mock_tables/state_db.json

+6
Original file line numberDiff line numberDiff line change
@@ -1144,5 +1144,11 @@
11441144
"STATUS": "up",
11451145
"REMOTE_MOD": "0",
11461146
"REMOTE_PORT": "93"
1147+
},
1148+
"ACL_TABLE_TABLE|DATAACL_5" : {
1149+
"status": "Active"
1150+
},
1151+
"ACL_RULE_TABLE|DATAACL_5|RULE_1" : {
1152+
"status": "Active"
11471153
}
11481154
}

tests/show_acl_test.py

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import os
2+
import pytest
3+
from click.testing import CliRunner
4+
5+
import acl_loader.main as acl_loader_show
6+
from acl_loader import *
7+
from acl_loader.main import *
8+
from importlib import reload
9+
10+
root_path = os.path.dirname(os.path.abspath(__file__))
11+
modules_path = os.path.dirname(root_path)
12+
scripts_path = os.path.join(modules_path, "scripts")
13+
14+
15+
@pytest.fixture()
16+
def setup_teardown_single_asic():
17+
os.environ["PATH"] += os.pathsep + scripts_path
18+
os.environ["UTILITIES_UNIT_TESTING"] = "2"
19+
os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = ""
20+
yield
21+
os.environ["UTILITIES_UNIT_TESTING"] = "0"
22+
23+
24+
@pytest.fixture(scope="class")
25+
def setup_teardown_multi_asic():
26+
os.environ["PATH"] += os.pathsep + scripts_path
27+
os.environ["UTILITIES_UNIT_TESTING"] = "2"
28+
os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = "multi_asic"
29+
from .mock_tables import mock_multi_asic_3_asics
30+
reload(mock_multi_asic_3_asics)
31+
from .mock_tables import dbconnector
32+
dbconnector.load_namespace_config()
33+
yield
34+
os.environ["UTILITIES_UNIT_TESTING"] = "0"
35+
os.environ["UTILITIES_UNIT_TESTING_TOPOLOGY"] = ""
36+
from .mock_tables import mock_single_asic
37+
reload(mock_single_asic)
38+
39+
40+
class TestShowACLSingleASIC(object):
41+
def test_show_acl_table(self, setup_teardown_single_asic):
42+
runner = CliRunner()
43+
aclloader = AclLoader()
44+
context = {
45+
"acl_loader": aclloader
46+
}
47+
result = runner.invoke(acl_loader_show.cli.commands['show'].commands['table'], ['DATAACL_5'], obj=context)
48+
assert result.exit_code == 0
49+
# We only care about the third line, which contains the 'Active'
50+
result_top = result.output.split('\n')[2]
51+
expected_output = "DATAACL_5 L3 Ethernet124 DATAACL_5 ingress Active"
52+
assert result_top == expected_output
53+
54+
def test_show_acl_rule(self, setup_teardown_single_asic):
55+
runner = CliRunner()
56+
aclloader = AclLoader()
57+
context = {
58+
"acl_loader": aclloader
59+
}
60+
result = runner.invoke(acl_loader_show.cli.commands['show'].commands['rule'], ['DATAACL_5'], obj=context)
61+
assert result.exit_code == 0
62+
# We only care about the third line, which contains the 'Active'
63+
result_top = result.output.split('\n')[2]
64+
expected_output = "DATAACL_5 RULE_1 9999 FORWARD IP_PROTOCOL: 126 Active"
65+
assert result_top == expected_output
66+
67+
68+
class TestShowACLMultiASIC(object):
69+
def test_show_acl_table(self, setup_teardown_multi_asic):
70+
runner = CliRunner()
71+
aclloader = AclLoader()
72+
context = {
73+
"acl_loader": aclloader
74+
}
75+
result = runner.invoke(acl_loader_show.cli.commands['show'].commands['table'], ['DATAACL_5'], obj=context)
76+
assert result.exit_code == 0
77+
# We only care about the third line, which contains the 'Active'
78+
result_top = result.output.split('\n')[2]
79+
expected_output = "DATAACL_5 L3 Ethernet124 DATAACL_5 ingress {'asic0': 'Active', 'asic2': 'Active'}"
80+
assert result_top == expected_output
81+
82+
def test_show_acl_rule(self, setup_teardown_multi_asic):
83+
runner = CliRunner()
84+
aclloader = AclLoader()
85+
context = {
86+
"acl_loader": aclloader
87+
}
88+
result = runner.invoke(acl_loader_show.cli.commands['show'].commands['rule'], ['DATAACL_5'], obj=context)
89+
assert result.exit_code == 0
90+
# We only care about the third line, which contains the 'Active'
91+
result_top = result.output.split('\n')[2]
92+
expected_output = "DATAACL_5 RULE_1 9999 FORWARD IP_PROTOCOL: 126 {'asic0': 'Active', 'asic2': 'Active'}"
93+
assert result_top == expected_output
94+
95+

0 commit comments

Comments
 (0)