Skip to content

Commit 26a632b

Browse files
Merge branch 'master' into dhcp_relay
2 parents 75ef029 + 89c9aef commit 26a632b

File tree

8 files changed

+135
-14
lines changed

8 files changed

+135
-14
lines changed

doc/Command-Reference.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5657,6 +5657,34 @@ The "fec-stats" subcommand is used to disply the interface fec related statistic
56575657
Ethernet16 U 0 0 0 1.77e-20 0.00e+00 1.37e-13
56585658
```
56595659

5660+
For debugging link related issues where you need to clear the FEC histogram and monitor the link again, use the following command
5661+
5662+
- Example (for all ports):
5663+
```
5664+
root@sonic:~# portstat -fh
5665+
Last cached time was 2025-10-02T16:43:57.934081
5666+
IFACE BIN0 BIN1 BIN2 BIN3 BIN4 BIN5 BIN6 BIN7 BIN8 BIN9 BIN10 BIN11 BIN12 BIN13 BIN14 BIN15
5667+
----------- ------------- ---------- --------- ------ ------ ------ ------ ------ ------ ------ ------- ------- ------- ------- ------- -------
5668+
Ethernet0 4,374,661,575 340 1 0 0 0 0 0 0 0 0 0 0 0 0 0
5669+
Ethernet8 4,374,590,263 8,069 9 0 0 0 0 0 0 0 0 0 0 0 0 0
5670+
Ethernet16 4,374,660,911 3,187 4 0 0 0 0 0 0 0 0 0 0 0 0 0
5671+
Ethernet24 4,374,594,305 57,484 502 0 0 0 0 0 0 0 0 0 0 0 0 0
5672+
Ethernet32 4,374,649,615 116 0 0 0 0 0 0 0 0 0 0 0 0 0 0
5673+
Ethernet40 4,374,650,913 1,212 1 0 0 0 0 0 0 0 0 0 0 0 0 0
5674+
```
5675+
5676+
- Example (for a particular port):
5677+
```
5678+
root@sonic:~# portstat -fh -i Ethernet504
5679+
Last cached time was 2025-10-02T16:43:57.934081
5680+
IFACE BIN0 BIN1 BIN2 BIN3 BIN4 BIN5 BIN6 BIN7 BIN8 BIN9 BIN10 BIN11 BIN12 BIN13 BIN14 BIN15
5681+
----------- ----------- ------ ------ ------ ------ ------ ------ ------ ------ ------ ------- ------- ------- ------- ------- -------
5682+
Ethernet504 624,891,017 13,331 172 0 0 0 0 0 0 0 0 0 0 0 0 0
5683+
root@str-7060x6-c09-u25:~#
5684+
```
5685+
5686+
To clear the FEC histogram use `portstat -c`. NOTE: This will clear all counters.
5687+
56605688
The "trim" subcommand is used to display the interface packet trimming related statistic.
56615689

56625690
- Example:

generic_config_updater/field_operation_validators.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ def _validate_field(field, port, value):
196196
return False
197197
return True
198198
if field == "speed":
199+
# For chassis, skip speed validation as desired speed is not in supported_speeds of StateDB.
200+
if device_info.is_chassis():
201+
return True
199202
supported_speeds_str = read_statedb_entry(scope, "PORT_TABLE", port, "supported_speeds") or ''
200203
try:
201204
supported_speeds = [int(s) for s in supported_speeds_str.split(',') if s]

scripts/generate_dump

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,7 @@ save_frr_info() {
794794
save_vtysh "show ipv6 route vrf all nexthop-group" "frr.ip6_route.nhg"
795795
save_vtysh "show zebra fpm stats" "frr.fpm.stats"
796796
save_vtysh "show zebra dplane detailed" "frr.dplane"
797+
save_vtysh "show zebra dplane providers" "frr.dplane.providers"
797798
save_vtysh "show interface vrf all" "frr.interfaces"
798799
save_vtysh "show zebra" "frr.zebra"
799800
save_vtysh "show zebra client" "frr.zebra.client"
@@ -807,8 +808,8 @@ save_frr_info() {
807808
save_vtysh "show mpls fec" "frr.mpls.fec"
808809
save_vtysh "show nexthop-group rib" "frr.nhg.rib"
809810
save_vtysh "show route-map" "frr.route_map"
810-
save_vtysh "show thread cpu" "frr.thread_cpu"
811-
save_vtysh "show thread poll" "frr.thread_poll"
811+
save_vtysh "show event cpu" "frr.event_cpu"
812+
save_vtysh "show event poll" "frr.event_poll"
812813
save_vtysh "show debugging hashtable" "frr.debugging_hashtable"
813814
save_vtysh "show work-queues" "frr.work_queues"
814815
save_vtysh "show memory" "frr.memory"

scripts/portstat

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ Examples:
7070
parser.add_argument('-D', '--delete-all', action='store_true', help='Delete all saved stats')
7171
parser.add_argument('-e', '--errors', action='store_true', help='Display interface errors')
7272
parser.add_argument('-f', '--fec-stats', action='store_true', help='Display FEC related statistics')
73+
parser.add_argument('-fh', '--fec_hist', action='store_true', help='Display FEC histogram')
7374
parser.add_argument('-j', '--json', action='store_true', help='Display in JSON format')
7475
parser.add_argument('-r', '--raw', action='store_true', help='Raw stats (unmodified output of netstat)')
7576
parser.add_argument('-R', '--rate', action='store_true', help='Display interface rates')
@@ -89,6 +90,7 @@ Examples:
8990
delete_all_stats = args.delete_all
9091
errors_only = args.errors
9192
fec_stats_only = args.fec_stats
93+
fec_hist_only = args.fec_hist
9294
rates_only = args.rate
9395
use_json = args.json
9496
raw_stats = args.raw
@@ -127,8 +129,10 @@ Examples:
127129

128130
# Now decide what information to display
129131
if raw_stats:
130-
portstat.cnstat_diff_print(cnstat_dict, {}, ratestat_dict, intf_list, use_json, print_all, errors_only,
131-
fec_stats_only, rates_only, trim_stats_only)
132+
portstat.cnstat_diff_print(cnstat_dict, {}, ratestat_dict, intf_list,
133+
use_json, print_all, errors_only,
134+
fec_stats_only, rates_only,
135+
trim_stats_only, fec_hist_only)
132136
sys.exit(0)
133137

134138
if save_fresh_stats:
@@ -147,22 +151,30 @@ Examples:
147151
cnstat_cached_dict = json.load(open(cnstat_fqn_file, 'r'))
148152
if not detail:
149153
print("Last cached time was " + str(cnstat_cached_dict.get('time')))
150-
portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, ratestat_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, trim_stats_only, detail, nonzero)
154+
portstat.cnstat_diff_print(cnstat_dict, cnstat_cached_dict, ratestat_dict,
155+
intf_list, use_json, print_all, errors_only,
156+
fec_stats_only, rates_only, trim_stats_only,
157+
fec_hist_only, detail, nonzero)
151158
except IOError as e:
152159
print(e.errno, e)
153160
else:
154161
if tag_name:
155162
print("\nFile '%s' does not exist" % cnstat_fqn_file)
156163
print("Did you run 'portstat -c -t %s' to record the counters via tag %s?\n" % (tag_name, tag_name))
157164
else:
158-
portstat.cnstat_diff_print(cnstat_dict, {}, ratestat_dict, intf_list, use_json, print_all, errors_only,
159-
fec_stats_only, rates_only, trim_stats_only, detail, nonzero)
165+
portstat.cnstat_diff_print(cnstat_dict, {}, ratestat_dict, intf_list,
166+
use_json, print_all, errors_only,
167+
fec_stats_only, rates_only, trim_stats_only,
168+
fec_hist_only, detail, nonzero)
160169
else:
161170
#wait for the specified time and then gather the new stats and output the difference.
162171
time.sleep(wait_time_in_seconds)
163172
print("The rates are calculated within %s seconds period" % wait_time_in_seconds)
164173
cnstat_new_dict, ratestat_new_dict = portstat.get_cnstat_dict()
165-
portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, ratestat_new_dict, intf_list, use_json, print_all, errors_only, fec_stats_only, rates_only, trim_stats_only, detail, nonzero)
174+
portstat.cnstat_diff_print(cnstat_new_dict, cnstat_dict, ratestat_new_dict,
175+
intf_list, use_json, print_all, errors_only,
176+
fec_stats_only, rates_only, trim_stats_only,
177+
fec_hist_only, detail, nonzero)
166178

167179
if __name__ == "__main__":
168180
main()

scripts/sfpshow

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@ class SFPShow(object):
328328
combined_dict.update(sfp_firmware_info_dict)
329329

330330
# Use the utility function to get sorted keys from combined dictionary
331-
sorted_sfp_info_keys = sorted(combined_dict.keys(), key=get_data_map_sort_key(combined_dict, data_map))
331+
sorted_sfp_info_keys = sorted(combined_dict.keys(),
332+
key=get_data_map_sort_key(combined_dict, data_map))
332333

333334
for key in sorted_sfp_info_keys:
334335
if key == 'cable_type':
@@ -583,7 +584,12 @@ class SFPShow(object):
583584
if field is None:
584585
return 'N/A'
585586
elif prefix in {'prefec_ber'}:
586-
return "{:.2E}".format(field) if field != 0 else '0.0'
587+
if isinstance(field, bool):
588+
return str(field)
589+
elif field != 0:
590+
return "{:.2E}".format(field)
591+
else:
592+
return '0.0'
587593
else:
588594
return str(field)
589595

tests/generic_config_updater/field_operation_validator_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,36 @@ def test_port_config_update_validator_valid_speed_existing_state_db(self):
3434
assert generic_config_updater.field_operation_validators.\
3535
port_config_update_validator(scope, patch_element) is True
3636

37+
@patch("sonic_py_common.device_info.is_chassis", mock.MagicMock(return_value=True))
38+
@patch("generic_config_updater.field_operation_validators.read_statedb_entry",
39+
mock.Mock(return_value="123,234"))
40+
def test_port_config_update_validator_invalid_speed_for_chassis(self):
41+
# 235 is in supported speeds, but for chassis, skip speed validation
42+
patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"speed": 235}}
43+
for scope in ["localhost", "asic0"]:
44+
assert generic_config_updater.field_operation_validators.\
45+
port_config_update_validator(scope, patch_element) is True
46+
47+
@patch("sonic_py_common.device_info.is_chassis", mock.MagicMock(return_value=False))
48+
@patch("generic_config_updater.field_operation_validators.read_statedb_entry",
49+
mock.Mock(return_value="123,234"))
50+
def test_port_config_update_validator_valid_speed_for_nonchassis(self):
51+
# 234 is not in supported speeds, but for chassis, skip speed validation
52+
patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"speed": 234}}
53+
for scope in ["localhost", "asic0"]:
54+
assert generic_config_updater.field_operation_validators.\
55+
port_config_update_validator(scope, patch_element) is True
56+
57+
@patch("sonic_py_common.device_info.is_chassis", mock.MagicMock(return_value=False))
58+
@patch("generic_config_updater.field_operation_validators.read_statedb_entry",
59+
mock.Mock(return_value="123,234"))
60+
def test_port_config_update_validator_invalid_speed_for_nonchassis(self):
61+
# 235 is not in supported speeds, but for chassis, skip speed validation
62+
patch_element = {"path": "/PORT/Ethernet3", "op": "add", "value": {"speed": 235}}
63+
for scope in ["localhost", "asic0"]:
64+
assert generic_config_updater.field_operation_validators.\
65+
port_config_update_validator(scope, patch_element) is False
66+
3767
@patch("generic_config_updater.field_operation_validators.read_statedb_entry",
3868
mock.Mock(return_value="123,234"))
3969
def test_port_config_update_validator_valid_speed_existing_state_db(self):

tests/sfp_test.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,8 @@
328328
DGD ps 5.37 5.56 5.81 7.0 7.0 False 0.0 0.0 False
329329
SOPMD ps^2 0.0 0.0 0.0 655.35 655.35 False 0.0 0.0 False
330330
SOP ROC krad/s 1.0 1.0 2.0 N/A N/A N/A N/A N/A N/A
331-
Pre-FEC BER N/A 4.58E-04 4.66E-04 5.76E-04 1.25E-02 1.10E-02 0.0 0.0 0.0 0.0
331+
Pre-FEC BER N/A 4.58E-04 4.66E-04 5.76E-04 1.25E-02 1.10E-02 False 0.0 0.0\
332+
False
332333
Post-FEC BER N/A 0.0 0.0 0.0 1000.0 1.0 False 0.0 0.0 False
333334
EVM % 100.0 100.0 100.0 N/A N/A N/A N/A N/A N/A
334335
"""
@@ -984,7 +985,7 @@ def test_qsfp_dd_eeprom_with_dom(self):
984985
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet8", "-d"])
985986
assert result.exit_code == 0
986987
assert result.output == test_qsfp_dd_eeprom_with_dom_output
987-
988+
988989
def test_osfp_eeprom_with_dom(self):
989990
runner = CliRunner()
990991
result = runner.invoke(show.cli.commands["interfaces"].commands["transceiver"].commands["eeprom"], ["Ethernet72", "-d"])

utilities_common/portstat.py

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,18 @@
2929
rx_jbr, rx_frag, rx_usize, rx_ovrrun,\
3030
fec_corr, fec_uncorr, fec_symbol_err,\
3131
wred_grn_drp_pkt, wred_ylw_drp_pkt, wred_red_drp_pkt, wred_tot_drp_pkt,\
32-
trim, trim_sent, trim_drop")
32+
trim, trim_sent, trim_drop, fec_bin0, fec_bin1, fec_bin2, fec_bin3,\
33+
fec_bin4, fec_bin5, fec_bin6, fec_bin7, fec_bin8, fec_bin9, fec_bin10,\
34+
fec_bin11, fec_bin12, fec_bin13, fec_bin14, fec_bin15")
3335
header_all = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR',
3436
'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR', 'TRIM', 'TRIM_TX', 'TRIM_DRP']
3537
header_std = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_UTIL', 'RX_ERR', 'RX_DRP', 'RX_OVR',
3638
'TX_OK', 'TX_BPS', 'TX_UTIL', 'TX_ERR', 'TX_DRP', 'TX_OVR']
3739
header_errors_only = ['IFACE', 'STATE', 'RX_ERR', 'RX_DRP', 'RX_OVR', 'TX_ERR', 'TX_DRP', 'TX_OVR']
3840
header_fec_only = ['IFACE', 'STATE', 'FEC_CORR', 'FEC_UNCORR', 'FEC_SYMBOL_ERR', 'FEC_PRE_BER',
3941
'FEC_POST_BER', 'FEC_PRE_BER_MAX']
42+
header_fec_hist_only = ['IFACE', 'BIN0', 'BIN1', 'BIN2', 'BIN3', 'BIN4', 'BIN5', 'BIN6', 'BIN7',
43+
'BIN8', 'BIN9', 'BIN10', 'BIN11', 'BIN12', 'BIN13', 'BIN14', 'BIN15']
4044
header_rates_only = ['IFACE', 'STATE', 'RX_OK', 'RX_BPS', 'RX_PPS', 'RX_UTIL', 'TX_OK', 'TX_BPS', 'TX_PPS', 'TX_UTIL']
4145
header_trim_only = ['IFACE', 'STATE', 'TRIM_PKTS', 'TRIM_TX_PKTS', 'TRIM_DRP_PKTS']
4246

@@ -112,6 +116,22 @@
112116
49: ['SAI_PORT_STAT_TRIM_PACKETS'],
113117
50: ['SAI_PORT_STAT_TX_TRIM_PACKETS'],
114118
51: ['SAI_PORT_STAT_DROPPED_TRIM_PACKETS'],
119+
52: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S0'],
120+
53: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S1'],
121+
54: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S2'],
122+
55: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S3'],
123+
56: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S4'],
124+
57: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S5'],
125+
58: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S6'],
126+
59: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S7'],
127+
60: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S8'],
128+
61: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S9'],
129+
62: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S10'],
130+
63: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S11'],
131+
64: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S12'],
132+
65: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S13'],
133+
66: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S14'],
134+
67: ['SAI_PORT_STAT_IF_IN_FEC_CODEWORD_ERRORS_S15'],
115135
}
116136

117137
STATUS_NA = 'N/A'
@@ -550,7 +570,7 @@ def cnstat_intf_diff_print(self, cnstat_new_dict, cnstat_old_dict, intf_list):
550570
def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict,
551571
ratestat_dict, intf_list, use_json,
552572
print_all, errors_only, fec_stats_only,
553-
rates_only, trim_stats_only, detail=False, nonzero=False):
573+
rates_only, trim_stats_only, fec_hist_only, detail=False, nonzero=False):
554574
"""
555575
Print the difference between two cnstat results.
556576
"""
@@ -638,6 +658,26 @@ def cnstat_diff_print(self, cnstat_new_dict, cnstat_old_dict,
638658
format_fec_ber(rates.fec_pre_ber),
639659
format_fec_ber(rates.fec_post_ber),
640660
format_fec_ber(rates.fec_pre_ber_max)))
661+
elif fec_hist_only:
662+
header = header_fec_hist_only
663+
664+
table.append((key, ns_diff(cntr['fec_bin0'], old_cntr['fec_bin0']),
665+
ns_diff(cntr['fec_bin1'], old_cntr['fec_bin1']),
666+
ns_diff(cntr['fec_bin2'], old_cntr['fec_bin2']),
667+
ns_diff(cntr['fec_bin3'], old_cntr['fec_bin3']),
668+
ns_diff(cntr['fec_bin4'], old_cntr['fec_bin4']),
669+
ns_diff(cntr['fec_bin5'], old_cntr['fec_bin5']),
670+
ns_diff(cntr['fec_bin6'], old_cntr['fec_bin6']),
671+
ns_diff(cntr['fec_bin7'], old_cntr['fec_bin7']),
672+
ns_diff(cntr['fec_bin8'], old_cntr['fec_bin8']),
673+
ns_diff(cntr['fec_bin9'], old_cntr['fec_bin9']),
674+
ns_diff(cntr['fec_bin10'], old_cntr['fec_bin10']),
675+
ns_diff(cntr['fec_bin11'], old_cntr['fec_bin11']),
676+
ns_diff(cntr['fec_bin12'], old_cntr['fec_bin12']),
677+
ns_diff(cntr['fec_bin13'], old_cntr['fec_bin13']),
678+
ns_diff(cntr['fec_bin14'], old_cntr['fec_bin14']),
679+
ns_diff(cntr['fec_bin15'], old_cntr['fec_bin15'])))
680+
641681
elif rates_only:
642682
header = header_rates_only
643683

0 commit comments

Comments
 (0)