From e1e5b6b6a80620bcf2343e41f9c1c6feaba6514d Mon Sep 17 00:00:00 2001 From: miatao Date: Mon, 20 Jul 2026 18:25:27 +0000 Subject: [PATCH 1/6] simplify pretty_print_local and rename pretty_print to pretty_print_tunnel Signed-off-by: miatao --- show/vnet.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/show/vnet.py b/show/vnet.py index 177e25187..eec85a4e3 100644 --- a/show/vnet.py +++ b/show/vnet.py @@ -456,17 +456,16 @@ def pretty_print_local(table, r, nexthop_val, ifname_val): i = 0 while i < max_entries: r.append(",".join(nexthops[i:i + row_width]) if i < len(nexthops) else "") - if interfaces: - r.append(",".join(interfaces[i:i + row_width]) if i < len(interfaces) else "") - else: - r.append(ifname_val if i == 0 else "") + r.append(",".join(interfaces[i:i + row_width]) if i < len(interfaces) else "") i += row_width table.append(r) r = ["", ""] -def pretty_print(table, r, epval, mac_addr, vni, metric, state): - endpoints = epval.split(',') +def pretty_print_tunnel(table, r, epval, mac_addr, vni, metric, state): + endpoints = epval.split(',') if epval else [] + if not endpoints: + endpoints = [""] # When mac_address or vni is a per-endpoint list, split so all three fields # wrap in the same chunks, keeps rows aligned at any ECMP scale. macs = mac_addr.split(',') if mac_addr and ',' in mac_addr else None From 79d66bcf1edb9d4e856d3c4ad7da98f28bcd60c9 Mon Sep 17 00:00:00 2001 From: miatao Date: Mon, 20 Jul 2026 20:02:44 +0000 Subject: [PATCH 2/6] Fix unit tests Signed-off-by: miatao --- show/vnet.py | 13 +++++++++---- tests/show_vnet_test.py | 14 +++++++------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/show/vnet.py b/show/vnet.py index eec85a4e3..85c90114d 100644 --- a/show/vnet.py +++ b/show/vnet.py @@ -578,13 +578,18 @@ def _show_tunnel_helper(vnet_name=None, appl_db=None, state_db=None): r.extend(k.split(":", 2)[1:]) state_db_key = '|'.join(k.split(":", 2)) val = appl_db.get_all(appl_db.APPL_DB, k) + if not val: + continue val_state = state_db.get_all(state_db.STATE_DB, state_db_key) epval = val.get('endpoint') + mac_addr = val.get('mac_address') or '' + vni = val.get('vni') or '' state = val_state.get('state') if val_state else "" raw_metric = val.get('metric') - metric = int(raw_metric) if raw_metric else '' - pretty_print(table, r, epval, - val.get('mac_address') or '', val.get('vni') or '', - metric, state) + try: + metric = int(raw_metric) if raw_metric else '' + except (ValueError, TypeError): + metric = raw_metric + pretty_print_tunnel(table, r, epval, mac_addr, vni, metric, state) click.echo(tabulate(table, tunnel_header)) diff --git a/tests/show_vnet_test.py b/tests/show_vnet_test.py index a42482877..ba6b028ee 100644 --- a/tests/show_vnet_test.py +++ b/tests/show_vnet_test.py @@ -22,14 +22,14 @@ def test_Preety_print(self): state = "active" epval = "fddd:a100:a251::a10:1,fddd:a101:a251::a10:1" - vnet.pretty_print(table, row, epval, mac_addr, vni, metric, state) + vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output = [['Vnet_v6_in_v6-0', 'fddd:a156:a251::a6:1/128', 'fddd:a100:a251::a10:1,fddd:a101:a251::a10:1', '', '', '0', 'active']] assert table == expected_output table =[] row = ["Vnet_v6_in_v6-0", "fddd:a156:a251::a6:1/128"] epval = "fddd:a100:a251::a10:1,fddd:a101:a251::a10:1,fddd:a100:a251::a11:1,fddd:a100:a251::a12:1,fddd:a100:a251::a13:1" - vnet.pretty_print(table, row, epval, mac_addr, vni, metric, state) + vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output = [ ['Vnet_v6_in_v6-0', 'fddd:a156:a251::a6:1/128', 'fddd:a100:a251::a10:1,fddd:a101:a251::a10:1', '', '', '0', 'active'], ['', '', 'fddd:a100:a251::a11:1,fddd:a100:a251::a12:1', '', '', '', ''], @@ -40,7 +40,7 @@ def test_Preety_print(self): table =[] row = ["Vnet_v6_in_v6-0", "fddd:a156:a251::a6:1/128"] epval = "192.168.1.1,192.168.1.2,192.168.1.3,192.168.1.4,192.168.1.5,192.168.1.6,192.168.1.7,192.168.1.8,192.168.1.9,192.168.1.10,192.168.1.11,192.168.1.12,192.168.1.13,192.168.1.14,192.168.1.15" - vnet.pretty_print(table, row, epval, mac_addr, vni, metric, state) + vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output =[ ['Vnet_v6_in_v6-0', 'fddd:a156:a251::a6:1/128', '192.168.1.1,192.168.1.2,192.168.1.3', '', '', '0', 'active'], ['', '', '192.168.1.4,192.168.1.5,192.168.1.6', '', '', '', ''], @@ -52,7 +52,7 @@ def test_Preety_print(self): table =[] row = ["Vnet_v6_in_v6-0", "fddd:a156:a251::a6:1/128"] epval = "192.168.1.1" - vnet.pretty_print(table, row, epval, mac_addr, vni, metric, state) + vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output =[ ['Vnet_v6_in_v6-0', 'fddd:a156:a251::a6:1/128', '192.168.1.1', '', '', '0', 'active']] assert table == expected_output @@ -65,7 +65,7 @@ def test_Preety_print(self): vni = "100,200,300,400" metric = "" # MAC items are 17 chars > 15, so row_width=2 - vnet.pretty_print(table, row, epval, mac_addr, vni, metric, state) + vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output = [ ["TestVnet", "10.0.0.1/32", "1.1.1.1,1.1.1.1", "aa:bb:cc:00:00:01,aa:bb:cc:00:00:02", "100,200", "", "active"], ["", "", "1.1.1.1,1.1.1.1", "aa:bb:cc:00:00:03,aa:bb:cc:00:00:04", "300,400", "", ""], @@ -80,7 +80,7 @@ def test_Preety_print(self): vni = "100,200,300" metric = "5" # All endpoints are <=7 chars, MAC items are 17 chars > 15 → row_width=2 - vnet.pretty_print(table, row, epval, mac_addr, vni, metric, state) + vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output = [ ["TestVnet", "10.0.0.1/32", "1.1.1.1,2.2.2.2", "aa:bb:cc:00:00:01,aa:bb:cc:00:00:02", "100,200", "5", "active"], ["", "", "3.3.3.3", "aa:bb:cc:00:00:03", "300", "", ""], @@ -103,7 +103,7 @@ def test_pretty_print_scale(self, N, vnet_name, prefix, metric): table = [] row = [vnet_name, prefix] - vnet.pretty_print(table, row, + vnet.pretty_print_tunnel(table, row, ",".join(endpoints_list), ",".join(macs_list), ",".join(vnis_list), From ba1f3c9caa6cd95089e2e5954b3b3ce889e272a8 Mon Sep 17 00:00:00 2001 From: miatao Date: Fri, 24 Jul 2026 05:33:40 +0000 Subject: [PATCH 3/6] adapt ecmp row wrapping to terminal size Signed-off-by: miatao --- show/vnet.py | 25 ++++++++++++-- tests/show_vnet_test.py | 72 +++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/show/vnet.py b/show/vnet.py index 85c90114d..cd61d1d18 100644 --- a/show/vnet.py +++ b/show/vnet.py @@ -1,4 +1,5 @@ import click +import shutil import utilities_common.cli as clicommon from natsort import natsorted from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector @@ -442,6 +443,22 @@ def routes(): pass +def _ecmp_row_width(max_item_len, num_wrap_cols, fixed_cols_est): + """Compute how many ECMP items fit per row to stay within the current terminal width. + + :param max_item_len: length of the longest individual item string + :param num_wrap_cols: number of columns that wrap (2 for nexthop+interface, + 3 for endpoint+mac+vni) + :param fixed_cols_est: estimated width of non-wrapping column content plus all + inter-column tabulate spacing (2 spaces × (ncols-1)) + """ + if max_item_len <= 0: + return 1 + terminal_cols = shutil.get_terminal_size((80, 24)).columns + row_width = (terminal_cols - fixed_cols_est) // (num_wrap_cols * (max_item_len + 1)) + return max(1, row_width) + + def pretty_print_local(table, r, nexthop_val, ifname_val): nexthops = [nexthop.strip() for nexthop in nexthop_val.split(',')] if nexthop_val else [] interfaces = [interface.strip() for interface in ifname_val.split(',')] if ifname_val else [] @@ -450,7 +467,9 @@ def pretty_print_local(table, r, nexthop_val, ifname_val): all_items = list(nexthops) + list(interfaces) max_len = max((len(item) for item in all_items), default=0) - row_width = 2 if max_len > 15 else 3 + # route_header: ['vnet name', 'prefix', 'nexthop', 'interface'] + # fixed_cols_est = vnet_name(~15) + prefix(~18) + 4-col spacing(6) = 39 + row_width = _ecmp_row_width(max_len, num_wrap_cols=2, fixed_cols_est=39) max_entries = max(len(nexthops), len(interfaces)) i = 0 @@ -479,7 +498,9 @@ def pretty_print_tunnel(table, r, epval, mac_addr, vni, metric, state): if vnis: all_items.extend(vnis) max_len = max((len(item) for item in all_items), default=0) - row_width = 2 if max_len > 15 else 3 + # tunnel_header: ['vnet name', 'prefix', 'endpoint', 'mac address', 'vni', 'metric', 'status'] + # fixed_cols_est = vnet_name(~15) + prefix(~16) + metric(6) + status(6) + 7-col spacing(12) = 55 + row_width = _ecmp_row_width(max_len, num_wrap_cols=3, fixed_cols_est=55) i = 0 while i < len(endpoints): diff --git a/tests/show_vnet_test.py b/tests/show_vnet_test.py index ba6b028ee..e65ad3504 100644 --- a/tests/show_vnet_test.py +++ b/tests/show_vnet_test.py @@ -1,17 +1,28 @@ # flake8: noqa: E501 import os import pytest +from unittest.mock import patch from click.testing import CliRunner from utilities_common.db import Db import show.main as show import show.vnet as vnet from tests.mock_tables import dbconnector +# Pin terminal width for all vnet route tests so row_width is deterministic. +_MOCK_TERMINAL = os.terminal_size((200, 24)) + + class TestShowVnetRoutesAll(object): @classmethod def setup_class(cls): print("SETUP") os.environ["UTILITIES_UNIT_TESTING"] = "1" + cls._terminal_patcher = patch("show.vnet.shutil.get_terminal_size", return_value=_MOCK_TERMINAL) + cls._terminal_patcher.start() + + @classmethod + def teardown_class(cls): + cls._terminal_patcher.stop() def test_Preety_print(self): table =[] @@ -64,7 +75,7 @@ def test_Preety_print(self): mac_addr = "aa:bb:cc:00:00:01,aa:bb:cc:00:00:02,aa:bb:cc:00:00:03,aa:bb:cc:00:00:04" vni = "100,200,300,400" metric = "" - # MAC items are 17 chars > 15, so row_width=2 + # MAC items are 17 chars; at T=200, row_width=(200-55)//(3*18)=2 vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output = [ ["TestVnet", "10.0.0.1/32", "1.1.1.1,1.1.1.1", "aa:bb:cc:00:00:01,aa:bb:cc:00:00:02", "100,200", "", "active"], @@ -72,14 +83,14 @@ def test_Preety_print(self): ] assert table == expected_output - # row_width decided by MAC item length, not just endpoint length + # row_width decided by MAC item length (max_len=17), not just endpoint length table = [] row = ["TestVnet", "10.0.0.1/32"] epval = "1.1.1.1,2.2.2.2,3.3.3.3" mac_addr = "aa:bb:cc:00:00:01,aa:bb:cc:00:00:02,aa:bb:cc:00:00:03" vni = "100,200,300" metric = "5" - # All endpoints are <=7 chars, MAC items are 17 chars > 15 → row_width=2 + # max_len=17 (MACs); at T=200, row_width=2 vnet.pretty_print_tunnel(table, row, epval, mac_addr, vni, metric, state) expected_output = [ ["TestVnet", "10.0.0.1/32", "1.1.1.1,2.2.2.2", "aa:bb:cc:00:00:01,aa:bb:cc:00:00:02", "100,200", "5", "active"], @@ -149,25 +160,23 @@ def test_pretty_print_local(self): vnet.pretty_print_local(table, row, "1.1.1.1, 2.2.2.2, 3.3.3.3", "Eth1, Eth2, Eth3") assert table == [["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3"]] - # 5 short nexthops — wraps into 2 rows (3+2) + # 5 short nexthops — at T=200, row_width=10, all 5 fit on one row table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4,5.5.5.5", "Eth1,Eth2,Eth3,Eth4,Eth5") assert table == [ - ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3"], - ["", "", "4.4.4.4,5.5.5.5", "Eth4,Eth5"], + ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4,5.5.5.5", "Eth1,Eth2,Eth3,Eth4,Eth5"], ] - # Long interface names (>15 chars) → row_width=2 + # PortChannel names (19 chars) — at T=200, row_width=4, all 3 fit on one row table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "10.33.254.1,10.33.254.3,10.33.254.5", "PortChannel1031.106,PortChannel1032.106,PortChannel1033.106") assert table == [ - ["TestVnet", "10.0.0.0/24", "10.33.254.1,10.33.254.3", "PortChannel1031.106,PortChannel1032.106"], - ["", "", "10.33.254.5", "PortChannel1033.106"], + ["TestVnet", "10.0.0.0/24", "10.33.254.1,10.33.254.3,10.33.254.5", "PortChannel1031.106,PortChannel1032.106,PortChannel1033.106"], ] def test_show_vnet_routes_all_basic(self): @@ -177,17 +186,15 @@ def test_show_vnet_routes_all_basic(self): result = runner.invoke(show.cli.commands['vnet'].commands['routes'].commands['all'], [], obj=db) assert result.exit_code == 0 expected_output = """\ -vnet name prefix nexthop interface ---------------- ---------------- -------------------------------------- -------------------------------- -Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3,10.33.254.5 Po1031.106,Po1032.106,Po1033.106 - 10.33.254.7,10.33.254.9,10.33.254.11 Po1034.106,Po1035.106,Po1036.106 - 10.33.254.13,10.33.254.15,10.33.254.17 Po1037.106,Po1038.106,Po1039.106 - 10.33.254.19,10.33.254.23,10.33.254.25 Po1040.106,Po1042.106,Po1043.106 - 10.33.254.27,10.33.254.29,10.33.254.31 Po1044.106,Po1045.106,Po1046.106 -test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 -test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 -test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 -test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2,100.103.4.3 Ethernet1,Ethernet2,Ethernet3 +vnet name prefix nexthop interface +--------------- ---------------- ----------------------------------------------------------------------------- ----------------------------------------------------------------- +Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3,10.33.254.5,10.33.254.7,10.33.254.9,10.33.254.11 Po1031.106,Po1032.106,Po1033.106,Po1034.106,Po1035.106,Po1036.106 + 10.33.254.13,10.33.254.15,10.33.254.17,10.33.254.19,10.33.254.23,10.33.254.25 Po1037.106,Po1038.106,Po1039.106,Po1040.106,Po1042.106,Po1043.106 + 10.33.254.27,10.33.254.29,10.33.254.31 Po1044.106,Po1045.106,Po1046.106 +test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 +test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 +test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 +test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2,100.103.4.3 Ethernet1,Ethernet2,Ethernet3 vnet name prefix endpoint mac address vni metric status ------------------ ------------------------ ------------------------------------------- ----------------------------------- --------------- -------- -------- @@ -279,17 +286,15 @@ def test_show_vnet_routes_local_basic(self): result = runner.invoke(show.cli.commands['vnet'].commands['routes'].commands['local'], [], obj=db) assert result.exit_code == 0 expected_output = """\ -vnet name prefix nexthop interface ---------------- ---------------- -------------------------------------- -------------------------------- -Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3,10.33.254.5 Po1031.106,Po1032.106,Po1033.106 - 10.33.254.7,10.33.254.9,10.33.254.11 Po1034.106,Po1035.106,Po1036.106 - 10.33.254.13,10.33.254.15,10.33.254.17 Po1037.106,Po1038.106,Po1039.106 - 10.33.254.19,10.33.254.23,10.33.254.25 Po1040.106,Po1042.106,Po1043.106 - 10.33.254.27,10.33.254.29,10.33.254.31 Po1044.106,Po1045.106,Po1046.106 -test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 -test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 -test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 -test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2,100.103.4.3 Ethernet1,Ethernet2,Ethernet3 +vnet name prefix nexthop interface +--------------- ---------------- ----------------------------------------------------------------------------- ----------------------------------------------------------------- +Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3,10.33.254.5,10.33.254.7,10.33.254.9,10.33.254.11 Po1031.106,Po1032.106,Po1033.106,Po1034.106,Po1035.106,Po1036.106 + 10.33.254.13,10.33.254.15,10.33.254.17,10.33.254.19,10.33.254.23,10.33.254.25 Po1037.106,Po1038.106,Po1039.106,Po1040.106,Po1042.106,Po1043.106 + 10.33.254.27,10.33.254.29,10.33.254.31 Po1044.106,Po1045.106,Po1046.106 +test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 +test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 +test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 +test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2,100.103.4.3 Ethernet1,Ethernet2,Ethernet3 """ assert result.output == expected_output @@ -331,10 +336,13 @@ def setup_class(cls): print("SETUP") os.environ["UTILITIES_UNIT_TESTING"] = "1" dbconnector.topo = "vnet_ecmp" + cls._terminal_patcher = patch("show.vnet.shutil.get_terminal_size", return_value=_MOCK_TERMINAL) + cls._terminal_patcher.start() @classmethod def teardown_class(cls): dbconnector.topo = None + cls._terminal_patcher.stop() def test_show_vnet_routes_tunnel_real_world_ecmp(self): """Real-world ECMP dataset: 287 entries (10 unique ep/MAC pairs repeated) for Vnet_7127926. @@ -349,7 +357,7 @@ def test_show_vnet_routes_tunnel_real_world_ecmp(self): assert result.exit_code == 0 output = result.output - # 30.0.21.0/24: 12 unique endpoints, 2-per-row (12 > 2 → row_width=2). Exact output pinned. + # 30.0.21.0/24: 12 unique endpoints. MAC items are 17 chars; at T=200, row_width=2. Exact output pinned. expected_21 = ( "Vnet_7127926 30.0.21.0/24 100.106.230.44,10.134.85.10 00:22:48:03:8c:f8,60:45:bd:a3:8d:ab 7127926,7127926 5 active\n" " 100.106.229.38,100.106.229.170 60:45:bd:a3:21:88,60:45:bd:a2:e4:39 7127926,7127926\n" From 45053070f302d5307ec6db2530185f8990798408 Mon Sep 17 00:00:00 2001 From: miatao Date: Fri, 24 Jul 2026 17:40:19 +0000 Subject: [PATCH 4/6] Fix copilot comments Signed-off-by: miatao --- tests/show_vnet_test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/show_vnet_test.py b/tests/show_vnet_test.py index e65ad3504..5469bab6c 100644 --- a/tests/show_vnet_test.py +++ b/tests/show_vnet_test.py @@ -1,9 +1,12 @@ # flake8: noqa: E501 import os -import pytest from unittest.mock import patch + +import pytest from click.testing import CliRunner + from utilities_common.db import Db + import show.main as show import show.vnet as vnet from tests.mock_tables import dbconnector @@ -148,7 +151,7 @@ def test_pretty_print_local(self): vnet.pretty_print_local(table, row, "192.168.1.1", "Ethernet1") assert table == [["TestVnet", "10.0.0.0/24", "192.168.1.1", "Ethernet1"]] - # 3 short nexthops, row_width=3, all fit on one row + # 3 short nexthops, all fit on one row table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3") From 9973d30a2bcbb5821d7272eeaa881a1fc5fdf405 Mon Sep 17 00:00:00 2001 From: miatao Date: Fri, 24 Jul 2026 19:09:24 +0000 Subject: [PATCH 5/6] Cap nexthops max as 2 per line Signed-off-by: miatao --- show/vnet.py | 21 +++++++++++---------- tests/show_vnet_test.py | 4 ++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/show/vnet.py b/show/vnet.py index cd61d1d18..9e11ed66a 100644 --- a/show/vnet.py +++ b/show/vnet.py @@ -1,10 +1,14 @@ -import click +import ipaddress import shutil -import utilities_common.cli as clicommon + +import click from natsort import natsorted from swsscommon.swsscommon import SonicV2Connector, ConfigDBConnector from tabulate import tabulate -import ipaddress + +from sonic_py_common import multi_asic +import utilities_common.cli as clicommon +import utilities_common.multi_asic as multi_asic_util # # 'vnet' command ("show vnet") @@ -460,16 +464,15 @@ def _ecmp_row_width(max_item_len, num_wrap_cols, fixed_cols_est): def pretty_print_local(table, r, nexthop_val, ifname_val): - nexthops = [nexthop.strip() for nexthop in nexthop_val.split(',')] if nexthop_val else [] + nexthops = [nexthop.strip() for nexthop in nexthop_val.split(',')] if nexthop_val else [""] interfaces = [interface.strip() for interface in ifname_val.split(',')] if ifname_val else [] - if not nexthops: - nexthops = [""] all_items = list(nexthops) + list(interfaces) max_len = max((len(item) for item in all_items), default=0) # route_header: ['vnet name', 'prefix', 'nexthop', 'interface'] # fixed_cols_est = vnet_name(~15) + prefix(~18) + 4-col spacing(6) = 39 - row_width = _ecmp_row_width(max_len, num_wrap_cols=2, fixed_cols_est=39) + # cap at 2 so nexthop/interface columns stay readable on any terminal width + row_width = min(2, _ecmp_row_width(max_len, num_wrap_cols=2, fixed_cols_est=39)) max_entries = max(len(nexthops), len(interfaces)) i = 0 @@ -482,9 +485,7 @@ def pretty_print_local(table, r, nexthop_val, ifname_val): def pretty_print_tunnel(table, r, epval, mac_addr, vni, metric, state): - endpoints = epval.split(',') if epval else [] - if not endpoints: - endpoints = [""] + endpoints = epval.split(',') if epval else [""] # When mac_address or vni is a per-endpoint list, split so all three fields # wrap in the same chunks, keeps rows aligned at any ECMP scale. macs = mac_addr.split(',') if mac_addr and ',' in mac_addr else None diff --git a/tests/show_vnet_test.py b/tests/show_vnet_test.py index 5469bab6c..362ce4fff 100644 --- a/tests/show_vnet_test.py +++ b/tests/show_vnet_test.py @@ -151,7 +151,7 @@ def test_pretty_print_local(self): vnet.pretty_print_local(table, row, "192.168.1.1", "Ethernet1") assert table == [["TestVnet", "10.0.0.0/24", "192.168.1.1", "Ethernet1"]] - # 3 short nexthops, all fit on one row + # 3 short nexthops — at T=200, row_width=10, all fit on one row table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3") @@ -172,7 +172,7 @@ def test_pretty_print_local(self): ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4,5.5.5.5", "Eth1,Eth2,Eth3,Eth4,Eth5"], ] - # PortChannel names (19 chars) — at T=200, row_width=4, all 3 fit on one row + # PortChannel interface names (19 chars) — at T=200, row_width=4, all 3 fit on one row table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, From c5e0113e63031bfe83b6089ed7aa695f7dfc6bdf Mon Sep 17 00:00:00 2001 From: miatao Date: Fri, 24 Jul 2026 22:01:20 +0000 Subject: [PATCH 6/6] Fix tests: update local-route assertions for row_width=2 cap Signed-off-by: miatao --- tests/show_vnet_test.py | 100 ++++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 46 deletions(-) diff --git a/tests/show_vnet_test.py b/tests/show_vnet_test.py index 362ce4fff..d3e2d7b3b 100644 --- a/tests/show_vnet_test.py +++ b/tests/show_vnet_test.py @@ -151,35 +151,44 @@ def test_pretty_print_local(self): vnet.pretty_print_local(table, row, "192.168.1.1", "Ethernet1") assert table == [["TestVnet", "10.0.0.0/24", "192.168.1.1", "Ethernet1"]] - # 3 short nexthops — at T=200, row_width=10, all fit on one row + # 3 short nexthops — row_width=2, wraps to 2 rows (2+1) table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3") - assert table == [["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3"]] + assert table == [ + ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2", "Eth1,Eth2"], + ["", "", "3.3.3.3", "Eth3"], + ] # Spaces in DB values are stripped table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "1.1.1.1, 2.2.2.2, 3.3.3.3", "Eth1, Eth2, Eth3") - assert table == [["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3", "Eth1,Eth2,Eth3"]] + assert table == [ + ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2", "Eth1,Eth2"], + ["", "", "3.3.3.3", "Eth3"], + ] - # 5 short nexthops — at T=200, row_width=10, all 5 fit on one row + # 5 short nexthops — row_width=2, wraps into 3 rows (2+2+1) table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4,5.5.5.5", "Eth1,Eth2,Eth3,Eth4,Eth5") assert table == [ - ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2,3.3.3.3,4.4.4.4,5.5.5.5", "Eth1,Eth2,Eth3,Eth4,Eth5"], + ["TestVnet", "10.0.0.0/24", "1.1.1.1,2.2.2.2", "Eth1,Eth2"], + ["", "", "3.3.3.3,4.4.4.4", "Eth3,Eth4"], + ["", "", "5.5.5.5", "Eth5"], ] - # PortChannel interface names (19 chars) — at T=200, row_width=4, all 3 fit on one row + # PortChannel interface names — row_width=2 table = [] row = ["TestVnet", "10.0.0.0/24"] vnet.pretty_print_local(table, row, "10.33.254.1,10.33.254.3,10.33.254.5", "PortChannel1031.106,PortChannel1032.106,PortChannel1033.106") assert table == [ - ["TestVnet", "10.0.0.0/24", "10.33.254.1,10.33.254.3,10.33.254.5", "PortChannel1031.106,PortChannel1032.106,PortChannel1033.106"], + ["TestVnet", "10.0.0.0/24", "10.33.254.1,10.33.254.3", "PortChannel1031.106,PortChannel1032.106"], + ["", "", "10.33.254.5", "PortChannel1033.106"], ] def test_show_vnet_routes_all_basic(self): @@ -189,15 +198,22 @@ def test_show_vnet_routes_all_basic(self): result = runner.invoke(show.cli.commands['vnet'].commands['routes'].commands['all'], [], obj=db) assert result.exit_code == 0 expected_output = """\ -vnet name prefix nexthop interface ---------------- ---------------- ----------------------------------------------------------------------------- ----------------------------------------------------------------- -Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3,10.33.254.5,10.33.254.7,10.33.254.9,10.33.254.11 Po1031.106,Po1032.106,Po1033.106,Po1034.106,Po1035.106,Po1036.106 - 10.33.254.13,10.33.254.15,10.33.254.17,10.33.254.19,10.33.254.23,10.33.254.25 Po1037.106,Po1038.106,Po1039.106,Po1040.106,Po1042.106,Po1043.106 - 10.33.254.27,10.33.254.29,10.33.254.31 Po1044.106,Po1045.106,Po1046.106 -test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 -test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 -test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 -test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2,100.103.4.3 Ethernet1,Ethernet2,Ethernet3 +vnet name prefix nexthop interface +--------------- ---------------- ------------------------- --------------------- +Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3 Po1031.106,Po1032.106 + 10.33.254.5,10.33.254.7 Po1033.106,Po1034.106 + 10.33.254.9,10.33.254.11 Po1035.106,Po1036.106 + 10.33.254.13,10.33.254.15 Po1037.106,Po1038.106 + 10.33.254.17,10.33.254.19 Po1039.106,Po1040.106 + 10.33.254.23,10.33.254.25 Po1042.106,Po1043.106 + 10.33.254.27,10.33.254.29 Po1044.106,Po1045.106 + 10.33.254.31 Po1046.106 +test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 +test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 +test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2 Ethernet1,Ethernet2 + 100.102.4.3 Ethernet3 +test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2 Ethernet1,Ethernet2 + 100.103.4.3 Ethernet3 vnet name prefix endpoint mac address vni metric status ------------------ ------------------------ ------------------------------------------- ----------------------------------- --------------- -------- -------- @@ -226,11 +242,12 @@ def test_show_vnet_routes_all_vnetname(self): ['test_v4_in_v4-0'], obj=db) assert result.exit_code == 0 expected_output = """\ -vnet name prefix nexthop interface ---------------- ---------------- ----------------------------------- ----------------------------- -test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 -test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 -test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 +vnet name prefix nexthop interface +--------------- ---------------- ----------------------- ------------------- +test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 +test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 +test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2 Ethernet1,Ethernet2 + 100.102.4.3 Ethernet3 vnet name prefix endpoint mac address vni metric status --------------- ---------------- ----------- ------------- ----- -------- -------- @@ -289,31 +306,22 @@ def test_show_vnet_routes_local_basic(self): result = runner.invoke(show.cli.commands['vnet'].commands['routes'].commands['local'], [], obj=db) assert result.exit_code == 0 expected_output = """\ -vnet name prefix nexthop interface ---------------- ---------------- ----------------------------------------------------------------------------- ----------------------------------------------------------------- -Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3,10.33.254.5,10.33.254.7,10.33.254.9,10.33.254.11 Po1031.106,Po1032.106,Po1033.106,Po1034.106,Po1035.106,Po1036.106 - 10.33.254.13,10.33.254.15,10.33.254.17,10.33.254.19,10.33.254.23,10.33.254.25 Po1037.106,Po1038.106,Po1039.106,Po1040.106,Po1042.106,Po1043.106 - 10.33.254.27,10.33.254.29,10.33.254.31 Po1044.106,Po1045.106,Po1046.106 -test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 -test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 -test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 -test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2,100.103.4.3 Ethernet1,Ethernet2,Ethernet3 -""" - assert result.output == expected_output - - def test_show_vnet_routes_local_vnetname(self): - runner = CliRunner() - db = Db() - - result = runner.invoke(show.cli.commands['vnet'].commands['routes'].commands['local'], - ['test_v4_in_v4-0'], obj=db) - assert result.exit_code == 0 - expected_output = """\ -vnet name prefix nexthop interface ---------------- ---------------- ----------------------------------- ----------------------------- -test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 -test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 -test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 +vnet name prefix nexthop interface +--------------- ---------------- ------------------------- --------------------- +Vnet_7959668 10.32.0.0/17 10.33.254.1,10.33.254.3 Po1031.106,Po1032.106 + 10.33.254.5,10.33.254.7 Po1033.106,Po1034.106 + 10.33.254.9,10.33.254.11 Po1035.106,Po1036.106 + 10.33.254.13,10.33.254.15 Po1037.106,Po1038.106 + 10.33.254.17,10.33.254.19 Po1039.106,Po1040.106 + 10.33.254.23,10.33.254.25 Po1042.106,Po1043.106 + 10.33.254.27,10.33.254.29 Po1044.106,Po1045.106 + 10.33.254.31 Po1046.106 +test_v4_in_v4-0 160.162.191.1/32 100.100.4.1 Ethernet1 +test_v4_in_v4-0 160.163.191.1/32 100.101.4.1,100.101.4.2 Ethernet1,Ethernet2 +test_v4_in_v4-0 160.164.191.1/32 100.102.4.1,100.102.4.2 Ethernet1,Ethernet2 + 100.102.4.3 Ethernet3 +test_v4_in_v4-1 160.165.191.1/32 100.103.4.1,100.103.4.2 Ethernet1,Ethernet2 + 100.103.4.3 Ethernet3 """ assert result.output == expected_output