From a51c336fcb680144839de64a52d740f63520f2e6 Mon Sep 17 00:00:00 2001 From: miatao Date: Wed, 6 May 2026 22:27:56 +0000 Subject: [PATCH] [show/vnet] Add pretty print wrapping for local/non-tunnel routes Signed-off-by: miatao (cherry picked from commit 2fc56e698adc71b4b5d7086073c85932eec022c0) --- show/vnet.py | 29 +++++++++-- tests/mock_tables/appl_db.json | 4 ++ tests/show_vnet_test.py | 94 ++++++++++++++++++++++++++-------- 3 files changed, 102 insertions(+), 25 deletions(-) diff --git a/show/vnet.py b/show/vnet.py index db87ee324..177e25187 100644 --- a/show/vnet.py +++ b/show/vnet.py @@ -442,6 +442,29 @@ def routes(): pass +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 [] + if not nexthops: + nexthops = [""] + + 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 + + max_entries = max(len(nexthops), len(interfaces)) + 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 "") + i += row_width + table.append(r) + r = ["", ""] + + def pretty_print(table, r, epval, mac_addr, vni, metric, state): endpoints = epval.split(',') # When mac_address or vni is a per-endpoint list, split so all three fields @@ -531,9 +554,9 @@ def _show_local_helper(vnet_name=None, appl_db=None): r = [] r.extend(k.split(":", 2)[1:]) val = appl_db.get_all(appl_db.APPL_DB, k) - r.append(val.get('nexthop')) - r.append(val.get('ifname')) - table.append(r) + nexthop_val = val.get('nexthop') or '' + ifname_val = val.get('ifname') or '' + pretty_print_local(table, r, nexthop_val, ifname_val) click.echo(tabulate(table, route_header)) diff --git a/tests/mock_tables/appl_db.json b/tests/mock_tables/appl_db.json index f0a6c020c..46211f967 100644 --- a/tests/mock_tables/appl_db.json +++ b/tests/mock_tables/appl_db.json @@ -389,6 +389,10 @@ "nexthop": "100.103.4.1, 100.103.4.2, 100.103.4.3", "ifname": "Ethernet1, Ethernet2, Ethernet3" }, + "VNET_ROUTE_TABLE:Vnet_7959668:10.32.0.0/17": { + "nexthop": "10.33.254.1,10.33.254.3,10.33.254.5,10.33.254.7,10.33.254.9,10.33.254.11,10.33.254.13,10.33.254.15,10.33.254.17,10.33.254.19,10.33.254.23,10.33.254.25,10.33.254.27,10.33.254.29,10.33.254.31", + "ifname": "Po1031.106,Po1032.106,Po1033.106,Po1034.106,Po1035.106,Po1036.106,Po1037.106,Po1038.106,Po1039.106,Po1040.106,Po1042.106,Po1043.106,Po1044.106,Po1045.106,Po1046.106" + }, "VNET_ROUTE_TUNNEL_TABLE:test_v4_in_v4-0:160.163.191.1/32": { "endpoint":"100.251.7.1", "endpoint_monitor":"100.251.7.1", diff --git a/tests/show_vnet_test.py b/tests/show_vnet_test.py index 75bd0f154..a42482877 100644 --- a/tests/show_vnet_test.py +++ b/tests/show_vnet_test.py @@ -130,6 +130,46 @@ def test_pretty_print_scale(self, N, vnet_name, prefix, metric): assert ",".join(r[3] for r in table) == ",".join(macs_list) assert ",".join(r[4] for r in table) == ",".join(vnis_list) + def test_pretty_print_local(self): + # Single nexthop, no wrapping + table = [] + row = ["TestVnet", "10.0.0.0/24"] + 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 + 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"]] + + # 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"]] + + # 5 short nexthops — wraps into 2 rows (3+2) + 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"], + ] + + # Long interface names (>15 chars) → 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", "PortChannel1031.106,PortChannel1032.106"], + ["", "", "10.33.254.5", "PortChannel1033.106"], + ] + def test_show_vnet_routes_all_basic(self): runner = CliRunner() db = Db() @@ -137,12 +177,17 @@ 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 ---------------- ---------------- ------------------------------------- ------------------------------- -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 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 endpoint mac address vni metric status ------------------ ------------------------ ------------------------------------------- ----------------------------------- --------------- -------- -------- @@ -171,11 +216,11 @@ 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,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 vnet name prefix endpoint mac address vni metric status --------------- ---------------- ----------- ------------- ----- -------- -------- @@ -234,12 +279,17 @@ 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 ---------------- ---------------- ------------------------------------- ------------------------------- -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 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 """ assert result.output == expected_output @@ -251,11 +301,11 @@ def test_show_vnet_routes_local_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,100.102.4.3 Ethernet1,Ethernet2,Ethernet3 """ assert result.output == expected_output