Skip to content

Commit b38898f

Browse files
jfeng-aristaarfeigin
authored andcommitted
[chassis][voq]Add fabric monitoring commands. (sonic-net#3239)
What I did Add a force option for config fabric port unisolate command. Add a show command to display if a up link is get isolated or not . Example output is : # show fabric isolation asic0 Local Link Auto Isolated Manual Isolated Isolated ------------ --------------- ----------------- ---------- 0 0 0 0 1 0 0 0 2 0 0 0 .... Add test for the new commands. The test is failed now as it needs this sonic-net/sonic-swss#3089 merged in first.
1 parent 0dcef84 commit b38898f

File tree

6 files changed

+194
-14
lines changed

6 files changed

+194
-14
lines changed

config/fabric.py

+46-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
import utilities_common.cli as clicommon
33
import utilities_common.multi_asic as multi_asic_util
44
from sonic_py_common import multi_asic
5-
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector
5+
from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector, APP_FABRIC_PORT_TABLE_NAME
6+
7+
FABRIC_PORT_STATUS_TABLE_PREFIX = APP_FABRIC_PORT_TABLE_NAME+"|"
8+
69

710
#
811
# 'config fabric ...'
@@ -66,19 +69,13 @@ def isolate(portid, namespace):
6669
#
6770
@port.command()
6871
@click.argument('portid', metavar='<portid>', required=True)
72+
@click.option('-f', '--force', is_flag=True, default=False, help='Force to unisolate a link even if it is auto isolated.')
6973
@multi_asic_util.multi_asic_click_option_namespace
70-
def unisolate(portid, namespace):
74+
def unisolate(portid, namespace, force):
7175
"""FABRIC PORT unisolate <portid>"""
7276

7377
ctx = click.get_current_context()
7478

75-
if not portid.isdigit():
76-
ctx.fail("Invalid portid")
77-
78-
n_asics = multi_asic.get_num_asics()
79-
if n_asics > 1 and namespace is None:
80-
ctx.fail('Must specify asic')
81-
8279
# Connect to config database
8380
config_db = ConfigDBConnector(use_unix_socket_path=True, namespace=namespace)
8481
config_db.connect()
@@ -87,6 +84,37 @@ def unisolate(portid, namespace):
8784
state_db = SonicV2Connector(use_unix_socket_path=True, namespace=namespace)
8885
state_db.connect(state_db.STATE_DB, False)
8986

87+
n_asics = multi_asic.get_num_asics()
88+
if n_asics > 1 and namespace is None:
89+
ctx.fail( 'Must specify asic' )
90+
91+
# If "all" is specified then unisolate all ports.
92+
if portid == "all":
93+
port_keys = state_db.keys(state_db.STATE_DB, FABRIC_PORT_STATUS_TABLE_PREFIX + '*')
94+
for port_key in port_keys:
95+
port_data = state_db.get_all(state_db.STATE_DB, port_key)
96+
if "REMOTE_PORT" in port_data:
97+
port_number = int( port_key.replace( "FABRIC_PORT_TABLE|PORT", "" ) )
98+
99+
# Make sure configuration data exists
100+
portName = f'Fabric{port_number}'
101+
portConfigData = config_db.get_all(config_db.CONFIG_DB, "FABRIC_PORT|" + portName)
102+
if not bool( portConfigData ):
103+
ctx.fail( "Fabric monitor configuration data not present" )
104+
105+
# Update entry
106+
config_db.mod_entry( "FABRIC_PORT", portName, {'isolateStatus': False} )
107+
if force:
108+
forceShutCnt = int( portConfigData['forceUnisolateStatus'] )
109+
forceShutCnt += 1
110+
config_db.mod_entry( "FABRIC_PORT", portName,
111+
{'forceUnisolateStatus': forceShutCnt})
112+
113+
return
114+
115+
if not portid.isdigit():
116+
ctx.fail( "Invalid portid" )
117+
90118
# check if the port is actually in use
91119
portName = f'PORT{portid}'
92120
portStateData = state_db.get_all(state_db.STATE_DB, "FABRIC_PORT_TABLE|" + portName)
@@ -102,6 +130,15 @@ def unisolate(portid, namespace):
102130
# Update entry
103131
config_db.mod_entry("FABRIC_PORT", portName, {'isolateStatus': False})
104132

133+
if force:
134+
forceShutCnt = int( portConfigData['forceUnisolateStatus'] )
135+
forceShutCnt += 1
136+
config_db.mod_entry( "FABRIC_PORT", portName,
137+
{'forceUnisolateStatus': forceShutCnt})
138+
139+
click.echo("Force unisolate the link.")
140+
click.echo("It will clear all fabric link monitoring status for this link!")
141+
105142
#
106143
# 'config fabric port monitor ...'
107144
#

scripts/fabricstat

+40
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,40 @@ class FabricReachability(FabricStat):
307307
print(tabulate(body, header, tablefmt='simple', stralign='right'))
308308
return
309309

310+
class FabricIsolation(FabricStat):
311+
def isolation_print(self):
312+
# Connect to database
313+
self.db = multi_asic.connect_to_all_dbs_for_ns(self.namespace)
314+
# Get the set of all fabric ports
315+
port_keys = self.db.keys(self.db.STATE_DB, FABRIC_PORT_STATUS_TABLE_PREFIX + '*')
316+
# Create a new dictionary. The keys are the local port values in integer format.
317+
# Only fabric ports that have remote port data are added.
318+
port_dict = {}
319+
for port_key in port_keys:
320+
port_data = self.db.get_all(self.db.STATE_DB, port_key)
321+
if "REMOTE_PORT" in port_data:
322+
port_number = int(port_key.replace("FABRIC_PORT_TABLE|PORT", ""))
323+
port_dict.update({port_number: port_data})
324+
# Create ordered table of fabric ports.
325+
header = ["Local Link", "Auto Isolated", "Manual Isolated", "Isolated"]
326+
auto_isolated = 0
327+
manual_isolated = 0
328+
isolated = 0
329+
body = []
330+
for port_number in sorted(port_dict.keys()):
331+
port_data = port_dict[port_number]
332+
if "AUTO_ISOLATED" in port_data:
333+
auto_isolated = port_data["AUTO_ISOLATED"]
334+
if "CONFIG_ISOLATED" in port_data:
335+
manual_isolated = port_data["CONFIG_ISOLATED"]
336+
if "ISOLATED" in port_data:
337+
isolated = port_data["ISOLATED"]
338+
body.append((port_number, auto_isolated, manual_isolated, isolated));
339+
if self.namespace:
340+
print(f"\n{self.namespace}")
341+
print(tabulate(body, header, tablefmt='simple', stralign='right'))
342+
return
343+
310344
def main():
311345
global cnstat_dir
312346
global cnstat_fqn_file_port
@@ -329,12 +363,14 @@ Examples:
329363
parser.add_argument('-r','--reachability', action='store_true', help='Display reachability, otherwise port stat')
330364
parser.add_argument('-n','--namespace', default=None, help='Display fabric ports counters for specific namespace')
331365
parser.add_argument('-e', '--errors', action='store_true', help='Display errors')
366+
parser.add_argument('-i','--isolation', action='store_true', help='Display fabric ports isolation status')
332367
parser.add_argument('-C','--clear', action='store_true', help='Copy & clear fabric counters')
333368
parser.add_argument('-D','--delete', action='store_true', help='Delete saved stats')
334369

335370
args = parser.parse_args()
336371
queue = args.queue
337372
reachability = args.reachability
373+
isolation_status = args.isolation
338374
namespace = args.namespace
339375
errors_only = args.errors
340376

@@ -362,6 +398,10 @@ Examples:
362398
stat = FabricReachability(ns)
363399
stat.reachability_print()
364400
return
401+
elif isolation_status:
402+
stat = FabricIsolation(ns)
403+
stat.isolation_print()
404+
return
365405
else:
366406
stat = FabricPortStat(ns)
367407
cnstat_dict = stat.get_cnstat_dict()

show/fabric.py

+12
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ def counters():
1313
"""Show fabric port counters"""
1414
pass
1515

16+
@fabric.group(invoke_without_command=True)
17+
@multi_asic_util.multi_asic_click_option_namespace
18+
@click.option('-e', '--errors', is_flag=True)
19+
def isolation(namespace, errors):
20+
"""Show fabric isolation status"""
21+
cmd = ['fabricstat', '-i']
22+
if namespace is not None:
23+
cmd += ['-n', str(namespace)]
24+
if errors:
25+
cmd += ["-e"]
26+
clicommon.run_command(cmd)
27+
1628
@fabric.group(invoke_without_command=True)
1729
@multi_asic_util.multi_asic_click_option_namespace
1830
@click.option('-e', '--errors', is_flag=True)

tests/config_fabric_test.py

+18-5
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,35 @@ def test_config_isolation(self, ctx):
4242
expect_result = 0
4343
assert operator.eq(result.exit_code, expect_result)
4444

45-
# Issue command "config fabric port isolate 1",
46-
# check if the result has the error message as port 1 is not in use.
47-
result = self.basic_check("port", ["isolate", "1"], ctx)
48-
assert "Port 1 is not in use" in result.output
49-
5045
# Issue command "config fabric port unisolate 0",
5146
# check if the result is expected.
5247
result = self.basic_check("port", ["unisolate", "0"], ctx)
5348
expect_result = 0
5449
assert operator.eq(result.exit_code, expect_result)
5550

51+
# Issue command "config fabric port unisolate 0",
52+
# check if the result is expected.
53+
result = self.basic_check("port", ["unisolate", "0", "--force"], ctx)
54+
expect_result = 0
55+
assert operator.eq(result.exit_code, expect_result)
56+
assert "Force unisolate the link" in result.output
57+
58+
# Issue command "config fabric port isolate 1",
59+
# check if the result has the error message as port 1 is not in use.
60+
result = self.basic_check("port", ["isolate", "1"], ctx)
61+
assert "Port 1 is not in use" in result.output
62+
5663
# Issue command "config fabric port unisolate 1",
5764
# check if the result has the error message as port 1 is not in use.
5865
result = self.basic_check("port", ["unisolate", "1"], ctx)
5966
assert "Port 1 is not in use" in result.output
6067

68+
# Issue command "config fabric port unisolate all -n asic1"
69+
# check if the result has the warning message
70+
result = self.basic_check("port", ["unisolate", "all", "--force"], ctx)
71+
expect_result = 0
72+
assert operator.eq(result.exit_code, expect_result)
73+
6174
def test_config_fabric_monitor_threshold(self, ctx):
6275
# Issue command "config fabric port monitor error threshold <#> <#>"
6376
# with an out of range number, check if the result has the error message.

tests/fabricstat_test.py

+45
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,37 @@
151151
7 0 93 up
152152
"""
153153

154+
multi_asic_fabric_isolation = """\
155+
156+
asic0
157+
Local Link Auto Isolated Manual Isolated Isolated
158+
------------ --------------- ----------------- ----------
159+
0 0 0 0
160+
2 0 0 0
161+
4 0 0 0
162+
6 0 0 0
163+
7 0 0 0
164+
165+
asic1
166+
Local Link Auto Isolated Manual Isolated Isolated
167+
------------ --------------- ----------------- ----------
168+
0 0 0 0
169+
4 0 0 0
170+
"""
171+
172+
multi_asic_fabric_isolation_asic0 = """\
173+
174+
asic0
175+
Local Link Auto Isolated Manual Isolated Isolated
176+
------------ --------------- ----------------- ----------
177+
0 0 0 0
178+
2 0 0 0
179+
4 0 0 0
180+
6 0 0 0
181+
7 0 0 0
182+
"""
183+
184+
154185
class TestFabricStat(object):
155186
@classmethod
156187
def setup_class(cls):
@@ -271,6 +302,20 @@ def test_multi_show_fabric_reachability_asic(self):
271302
assert return_code == 0
272303
assert result == multi_asic_fabric_reachability_asic0
273304

305+
def test_multi_show_fabric_isolation(self):
306+
return_code, result = get_result_and_return_code(['fabricstat', '-i'])
307+
print("return_code: {}".format(return_code))
308+
print("result = {}".format(result))
309+
assert return_code == 0
310+
assert result == multi_asic_fabric_isolation
311+
312+
def test_multi_show_fabric_isolation_asic(self):
313+
return_code, result = get_result_and_return_code(['fabricstat', '-i', '-n', 'asic0'])
314+
print("return_code: {}".format(return_code))
315+
print("result = {}".format(result))
316+
assert return_code == 0
317+
assert result == multi_asic_fabric_isolation_asic0
318+
274319
@classmethod
275320
def teardown_class(cls):
276321
print("TEARDOWN")

tests/mock_tables/config_db.json

+33
Original file line numberDiff line numberDiff line change
@@ -2710,19 +2710,52 @@
27102710
},
27112711
"FABRIC_PORT|Fabric0": {
27122712
"alias": "Fabric0",
2713+
"forceUnisolateStatus": "0",
27132714
"isolateStatus": "False",
27142715
"lanes": "0"
27152716
},
27162717
"FABRIC_PORT|Fabric1": {
27172718
"alias": "Fabric1",
2719+
"forceUnisolateStatus": "0",
27182720
"isolateStatus": "False",
27192721
"lanes": "1"
27202722
},
27212723
"FABRIC_PORT|Fabric2": {
27222724
"alias": "Fabric2",
2725+
"forceUnisolateStatus": "0",
27232726
"isolateStatus": "False",
27242727
"lanes": "2"
27252728
},
2729+
"FABRIC_PORT|Fabric3": {
2730+
"alias": "Fabric3",
2731+
"forceUnisolateStatus": "0",
2732+
"isolateStatus": "False",
2733+
"lanes": "3"
2734+
},
2735+
"FABRIC_PORT|Fabric4": {
2736+
"alias": "Fabric4",
2737+
"forceUnisolateStatus": "0",
2738+
"isolateStatus": "False",
2739+
"lanes": "4"
2740+
},
2741+
"FABRIC_PORT|Fabric5": {
2742+
"alias": "Fabric5",
2743+
"forceUnisolateStatus": "0",
2744+
"isolateStatus": "False",
2745+
"lanes": "5"
2746+
},
2747+
"FABRIC_PORT|Fabric6": {
2748+
"alias": "Fabric6",
2749+
"forceUnisolateStatus": "0",
2750+
"isolateStatus": "False",
2751+
"lanes": "6"
2752+
},
2753+
"FABRIC_PORT|Fabric7": {
2754+
"alias": "Fabric7",
2755+
"forceUnisolateStatus": "0",
2756+
"isolateStatus": "False",
2757+
"lanes": "7"
2758+
},
27262759
"DHCP_RELAY|Vlan1000": {
27272760
"dhcpv6_servers": [
27282761
"fc02:2000::1"

0 commit comments

Comments
 (0)