Skip to content

Commit f5f55d5

Browse files
authored
Add CLI commands for bgp aggregate feature (#295)
<!-- Please make sure you've read and understood our contributing guidelines: https://github.com/Azure/SONiC/blob/gh-pages/CONTRIBUTING.md failure_prs.log skip_prs.log Make sure all your commits include a signature generated with `git commit -s` ** If this is a bug fix, make sure your description includes "closes #xxxx", "fixes #xxxx" or "resolves #xxxx" so that GitHub automatically closes the related issue when the PR is merged. If you are adding/modifying/removing any command or utility script, please also make sure to add/modify/remove any unit tests from the tests directory as appropriate. If you are modifying or removing an existing 'show', 'config' or 'sonic-clear' subcommand, or you are adding a new subcommand, please make sure you also update the Command Line Reference Guide (doc/Command-Reference.md) to reflect your changes. Please provide the following information: --> #### What I did Add CLI commands for bgp aggregate feature The HLD design is at https://github.com/sonic-net/SONiC/blob/master/doc/BGP/BGP-route-aggregation-with-bbr-awareness.md#cli-design <img width="1117" height="685" alt="image" src="https://github.com/user-attachments/assets/f74fe2f9-5f30-4214-900a-b00260d20199" /> #### How I did it Files Modified 1. utilities_common/bgp.py — Added CFG_BGP_AGGREGATE_ADDRESS = "BGP_AGGREGATE_ADDRESS" constant 2. config/bgp_cli.py — Added: • validate_ip_prefix() callback for IP prefix validation • AGGREGATE_ADDRESS click group (config bgp aggregate-address) • add command with options: --bbr-required, --summary-only, --as-set, --aggregate-address-prefix-list, --contributing-address-prefix-list • remove command taking an address argument • Both commands check for duplicate/missing entries before modifying Config DB 3. config/main.py — Registered bgp_cli.AGGREGATE_ADDRESS under the bgp group 4. show/bgp_cli.py — Added show_aggregate_address(db, af) helper that: • Reads BGP_AGGREGATE_ADDRESS from Config DB • Reads state from State DB when available • Filters entries by address family (ipv4/ipv6) • Displays flags: A=As Set, B=BBR Required, S=Summary Only 5. show/bgp_frr_v4.py — Added aggregate-address subcommand under show ip bgp 6. show/bgp_frr_v6.py — Added aggregate-address subcommand under show ipv6 bgp Files Created 1. tests/bgp_aggregate_address_test.py — Unit tests for add/remove/show commands (10 test cases) 2. tests/bgp_input/mock_config/aggregate_address.json — Mock Config DB with v4+v6 entries 3. tests/bgp_input/mock_config/aggregate_address_empty.json — Empty mock Config DB 4. tests/bgp_input/assert_show_output.py — Expected show output strings for v4, v6, and empty cases #### How to verify it 1. Add UT 2. Verified on DUT devices: ``` admin@sonic:~$ show ip bgp aggregate-address Flags: A - As Set, B - BBR Required, S - Summary Only Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List -------------- ------- -------------- ------------------------------- ---------------------------------- 192.168.0.0/24 Active B admin@sonic:~$ sudo config bgp aggregate-address add 192.168.0.0/24 --summary-only --as-set Usage: config bgp aggregate-address add [OPTIONS] ADDRESS Try 'config bgp aggregate-address add -h' for help. Error: Aggregate address '192.168.0.0/24' already exists admin@sonic:~$ sudo config bgp aggregate-address remove 192.168.0.0/24 admin@sonic:~$ sudo config bgp aggregate-address add 192.168.0.0/24 --summary-only --as-set admin@sonic:~$ show ip bgp aggregate-address Flags: A - As Set, B - BBR Required, S - Summary Only Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List -------------- ------- -------------- ------------------------------- ---------------------------------- 192.168.0.0/24 Active A,S admin@sonic:~$ sudo config bgp aggregate-address add fc00:1::/64 --bbr-required --aggregate-address-prefix-list AGG_ROUTE_V4 --contributing-address-prefix-list CONTRIBUTING_ROUTE_V4 admin@sonic:~$ show ip bgp aggregate-address Flags: A - As Set, B - BBR Required, S - Summary Only Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List -------------- ------- -------------- ------------------------------- ---------------------------------- 192.168.0.0/24 Active A,S admin@sonic:~$ show ipv6 bgp aggregate-address Flags: A - As Set, B - BBR Required, S - Summary Only Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List ----------- ------- -------------- ------------------------------- ---------------------------------- fc00:1::/64 Active B AGG_ROUTE_V4 CONTRIBUTING_ROUTE_V4 admin@sonic:~$ sudo config bgp aggregate-address remove fc00:1::/64 admin@sonic:~$ show ipv6 bgp aggregate-address Flags: A - As Set, B - BBR Required, S - Summary Only Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List -------- ------- -------------- ------------------------------- ---------------------------------- admin@sonic:~$ sudo config bgp aggregate-address add fc00:1::/64 --bbr-required --aggregate-address-prefix-list AGG_ROUTE_V4 --contributing-address-prefix-list CONTRIBUTING_ROUTE_V4 admin@sonic:~$ sudo config bgp aggregate-address add fc00:2::/64 --bbr-required --aggregate-address-prefix-list AGG_ROUTE_V4 --contributing-address-prefix-list CONTRIBUTING_ROUTE_V4 admin@sonic:~$sudo config bgp aggregate-address add fc00:9::/64 --bbr-required --aggregate-address-prefix-list AGG_ROUTE_V4 --contributing-address-prefix-list *$CONTRIBUTING_ROUTE_V4_#$#$# Error: '*#00' is invalid for contributing-address-prefix-list: only alphanumeric characters, underscores and hyphens are allowed admin@sonic:~$ show ipv6 bgp aggregate-address Flags: A - As Set, B - BBR Required, S - Summary Only Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List ----------- ------- -------------- ------------------------------- ---------------------------------- fc00:1::/64 Active B AGG_ROUTE_V4 CONTRIBUTING_ROUTE_V4 fc00:2::/64 Active B AGG_ROUTE_V4 CONTRIBUTING_ROUTE_V4 admin@sonic:~$ show ipv6 bgp -h Usage: show ipv6 bgp [OPTIONS] COMMAND [ARGS]... Show IPv6 BGP (Border Gateway Protocol) information Options: -?, -h, --help Show this message and exit. Commands: aggregate-address Show IPv6 BGP aggregate addresses neighbors Show IPv6 BGP neighbors network Show BGP ipv6 network summary Show summarized information of IPv6 BGP state vrf Show IPv6 BGP information for a given VRF admin@sonic:~$ sudo config bgp -h Usage: config bgp [OPTIONS] COMMAND [ARGS]... BGP-related configuration tasks Options: -?, -h, --help Show this message and exit. Commands: aggregate-address Configure BGP aggregate addresses device-global Configure BGP device global state remove Remove BGP neighbor configuration from the device shutdown Shut down BGP session(s) startup Start up BGP session(s) ``` Signed-off-by: Sonic Build Admin <sonicbld@microsoft.com> #### Previous command output (if the output of a command-line utility has changed) #### New command output (if the output of a command-line utility has changed)
1 parent fd161e5 commit f5f55d5

11 files changed

Lines changed: 847 additions & 0 deletions

File tree

config/bgp_cli.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import re
12
import click
3+
import ipaddress
24
import utilities_common.cli as clicommon
35

46
from sonic_py_common import logger
57
from utilities_common.bgp import (
68
CFG_BGP_DEVICE_GLOBAL,
79
BGP_DEVICE_GLOBAL_KEY,
10+
CFG_BGP_AGGREGATE_ADDRESS,
811
SYSLOG_IDENTIFIER,
912
to_str,
1013
)
@@ -190,3 +193,119 @@ def DEVICE_GLOBAL_WCMP_DISABLED(ctx, db):
190193
""" Disable Weighted-Cost Multi-Path (W-ECMP) feature """
191194

192195
wcmp_handler(ctx, db, "false")
196+
197+
198+
#
199+
# BGP aggregate-address ------------------------------------------------------------------------------------------------
200+
#
201+
202+
203+
PREFIX_LIST_PATTERN = re.compile(r'^[0-9a-zA-Z_-]*$')
204+
PREFIX_LIST_MAX_LEN = 128
205+
206+
207+
def validate_ip_prefix(ctx, param, value):
208+
""" Validate that the argument is a valid IP prefix """
209+
try:
210+
return str(ipaddress.ip_network(value, strict=False))
211+
except ValueError:
212+
raise click.BadParameter("'{}' is not a valid IP prefix".format(value))
213+
214+
215+
def validate_prefix_list_name(name, field_name):
216+
""" Validate prefix list name against YANG schema constraints.
217+
Pattern: [0-9a-zA-Z_-]*, max length: 128
218+
"""
219+
if len(name) > PREFIX_LIST_MAX_LEN:
220+
raise click.ClickException(
221+
"'{}' is invalid for {}: length exceeds {}".format(
222+
name, field_name, PREFIX_LIST_MAX_LEN))
223+
if not PREFIX_LIST_PATTERN.match(name):
224+
raise click.ClickException(
225+
"'{}' is invalid for {}: only alphanumeric characters, "
226+
"underscores and hyphens are allowed".format(name, field_name))
227+
228+
229+
@click.group(
230+
name="aggregate-address",
231+
cls=clicommon.AliasedGroup
232+
)
233+
def AGGREGATE_ADDRESS():
234+
""" Configure BGP aggregate addresses """
235+
236+
pass
237+
238+
239+
@AGGREGATE_ADDRESS.command(
240+
name="add"
241+
)
242+
@click.argument("address", callback=validate_ip_prefix)
243+
@click.option("--bbr-required", is_flag=True, default=False,
244+
help="Set if BBR is required for generating aggregate address")
245+
@click.option("--summary-only", is_flag=True, default=False,
246+
help="Only advertise the summary of aggregate address")
247+
@click.option("--as-set", is_flag=True, default=False,
248+
help="Include the AS set when advertising the aggregated address")
249+
@click.option("--aggregate-address-prefix-list", default="",
250+
help="Prefix list to append aggregated address to")
251+
@click.option("--contributing-address-prefix-list", default="",
252+
help="Prefix list to append contributing address filter to")
253+
@clicommon.pass_db
254+
@click.pass_context
255+
def AGGREGATE_ADDRESS_ADD(ctx, db, address, bbr_required, summary_only, as_set,
256+
aggregate_address_prefix_list, contributing_address_prefix_list):
257+
""" Add a BGP aggregate address """
258+
259+
table = CFG_BGP_AGGREGATE_ADDRESS
260+
key = address
261+
262+
# Validate prefix list names against YANG schema
263+
validate_prefix_list_name(aggregate_address_prefix_list,
264+
"aggregate-address-prefix-list")
265+
validate_prefix_list_name(contributing_address_prefix_list,
266+
"contributing-address-prefix-list")
267+
268+
# Check if entry already exists
269+
cfg = db.cfgdb.get_config()
270+
if table in cfg and key in cfg[table]:
271+
ctx.fail("Aggregate address '{}' already exists".format(key))
272+
273+
data = {
274+
"bbr-required": "true" if bbr_required else "false",
275+
"summary-only": "true" if summary_only else "false",
276+
"as-set": "true" if as_set else "false",
277+
"aggregate-address-prefix-list": aggregate_address_prefix_list,
278+
"contributing-address-prefix-list": contributing_address_prefix_list,
279+
}
280+
281+
try:
282+
db.cfgdb.set_entry(table, key, data)
283+
log.log_notice("Added BGP aggregate address: {}".format(key))
284+
except Exception as e:
285+
log.log_error("Failed to add BGP aggregate address '{}': {}".format(key, str(e)))
286+
ctx.fail(str(e))
287+
288+
289+
@AGGREGATE_ADDRESS.command(
290+
name="remove"
291+
)
292+
@click.argument("address", callback=validate_ip_prefix)
293+
@clicommon.pass_db
294+
@click.pass_context
295+
def AGGREGATE_ADDRESS_REMOVE(ctx, db, address):
296+
""" Remove a BGP aggregate address """
297+
298+
table = CFG_BGP_AGGREGATE_ADDRESS
299+
key = address
300+
301+
# Check if entry exists
302+
cfg = db.cfgdb.get_config()
303+
if table not in cfg or key not in cfg[table]:
304+
ctx.fail("Aggregate address '{}' does not exist".format(key))
305+
306+
try:
307+
db.cfgdb.set_entry(table, key, None)
308+
log.log_notice("Removed BGP aggregate address: {}".format(key))
309+
except Exception as e:
310+
log.log_error("Failed to remove BGP aggregate address '{}': {}".format(key, str(e)))
311+
ctx.fail(str(e))

config/main.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4750,6 +4750,7 @@ def bgp():
47504750

47514751
# BGP module extensions
47524752
config.commands['bgp'].add_command(bgp_cli.DEVICE_GLOBAL)
4753+
config.commands['bgp'].add_command(bgp_cli.AGGREGATE_ADDRESS)
47534754

47544755
#
47554756
# 'shutdown' subgroup ('config bgp shutdown ...')

doc/Command-Reference.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2786,6 +2786,46 @@ This command displays BGP device global configuration.
27862786
enabled enabled
27872787
```
27882788

2789+
**show ip bgp aggregate-address**
2790+
2791+
This command displays configured IPv4 BGP aggregate addresses from the CONFIG_DB.
2792+
2793+
- Usage:
2794+
```
2795+
show ip bgp aggregate-address
2796+
```
2797+
2798+
- Example:
2799+
```
2800+
admin@sonic:~$ show ip bgp aggregate-address
2801+
Flags: A - As Set, B - BBR Required, S - Summary Only
2802+
2803+
Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List
2804+
-------------- ------- -------------- ------------------------------- ----------------------------------
2805+
10.0.0.0/24 N/A A,B,S
2806+
192.168.0.0/24 Active B AGG_ROUTES_V4 AGG_CONTRIBUTING_ROUTES_V4
2807+
```
2808+
2809+
**show ipv6 bgp aggregate-address**
2810+
2811+
This command displays configured IPv6 BGP aggregate addresses from the CONFIG_DB.
2812+
2813+
- Usage:
2814+
```
2815+
show ipv6 bgp aggregate-address
2816+
```
2817+
2818+
- Example:
2819+
```
2820+
admin@sonic:~$ show ipv6 bgp aggregate-address
2821+
Flags: A - As Set, B - BBR Required, S - Summary Only
2822+
2823+
Prefix State Option Flags Aggregate Address Prefix List Contributing Address Prefix List
2824+
----------- ------- -------------- ------------------------------- ----------------------------------
2825+
fc00:1::/64 Active A AGG_ROUTES_V6 AGG_CONTRIBUTING_ROUTES_V6
2826+
fc00:3::/64 N/A B,S
2827+
```
2828+
27892829
Go Back To [Beginning of the document](#) or [Beginning of this section](#bgp)
27902830

27912831
### BGP config commands
@@ -2916,6 +2956,45 @@ Feature list:
29162956
admin@sonic:~$ config bgp device-global w-ecmp enabled
29172957
```
29182958

2959+
**config bgp aggregate-address add**
2960+
2961+
This command adds a BGP aggregate address entry to the CONFIG_DB.
2962+
2963+
- Usage:
2964+
```
2965+
config bgp aggregate-address add <ip-prefix> [--bbr-required] [--summary-only] [--as-set]
2966+
[--aggregate-address-prefix-list <name>] [--contributing-address-prefix-list <name>]
2967+
```
2968+
2969+
- Options:
2970+
- _--bbr-required_: set if BBR is required for generating aggregate address
2971+
- _--summary-only_: only advertise the summary of aggregate address
2972+
- _--as-set_: include the AS set when advertising the aggregated address
2973+
- _--aggregate-address-prefix-list_: prefix list to match aggregated address
2974+
- _--contributing-address-prefix-list_: prefix list to filter contributing addresses
2975+
2976+
- Examples:
2977+
```
2978+
admin@sonic:~$ sudo config bgp aggregate-address add 192.168.0.0/24 --bbr-required --summary-only
2979+
```
2980+
```
2981+
admin@sonic:~$ sudo config bgp aggregate-address add fc00:1::/64 --as-set --aggregate-address-prefix-list AGG_ROUTES_V6 --contributing-address-prefix-list AGG_CONTRIBUTING_ROUTES_V6
2982+
```
2983+
2984+
**config bgp aggregate-address remove**
2985+
2986+
This command removes a BGP aggregate address entry from the CONFIG_DB.
2987+
2988+
- Usage:
2989+
```
2990+
config bgp aggregate-address remove <ip-prefix>
2991+
```
2992+
2993+
- Example:
2994+
```
2995+
admin@sonic:~$ sudo config bgp aggregate-address remove 192.168.0.0/24
2996+
```
2997+
29192998
Go Back To [Beginning of the document](#) or [Beginning of this section](#bgp)
29202999

29213000
## Console

show/bgp_cli.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import click
2+
import ipaddress
23
import tabulate
34
import json
45
import utilities_common.cli as clicommon
56

67
from utilities_common.bgp import (
78
CFG_BGP_DEVICE_GLOBAL,
89
BGP_DEVICE_GLOBAL_KEY,
10+
CFG_BGP_AGGREGATE_ADDRESS,
911
to_str,
1012
)
1113

@@ -126,3 +128,87 @@ def DEVICE_GLOBAL(ctx, db, json_format):
126128
body.append(row)
127129

128130
click.echo(tabulate.tabulate(body, header))
131+
132+
133+
#
134+
# BGP aggregate-address show helper ------------------------------------------------------------------------------------
135+
#
136+
137+
138+
def show_aggregate_address(db, af):
139+
""" Show BGP aggregate addresses filtered by address family.
140+
141+
Args:
142+
db: Database object.
143+
af (str): Address family - "ipv4" or "ipv6".
144+
"""
145+
146+
header = [
147+
"Prefix",
148+
"State",
149+
"Option Flags",
150+
"Aggregate Address Prefix List",
151+
"Contributing Address Prefix List",
152+
]
153+
body = []
154+
155+
cfg_table = db.cfgdb.get_table(CFG_BGP_AGGREGATE_ADDRESS)
156+
157+
if not cfg_table:
158+
click.echo("Flags: A - As Set, B - BBR Required, S - Summary Only\n")
159+
click.echo(tabulate.tabulate(body, header))
160+
return
161+
162+
# Try to get state DB entries for state information
163+
state_table = {}
164+
try:
165+
state_db = db.db
166+
state_db.connect(state_db.STATE_DB, False)
167+
keys = state_db.keys(state_db.STATE_DB, f"{CFG_BGP_AGGREGATE_ADDRESS}|*")
168+
if keys:
169+
for key in keys:
170+
entry_key = key.split("|", 1)[1]
171+
state_table[entry_key] = state_db.get_all(state_db.STATE_DB, key)
172+
except Exception as exec:
173+
click.echo(f"Warning:failed to read BGP aggregate state from STATE_DB: {exec}\n", err=True)
174+
175+
for prefix in sorted(cfg_table.keys(),
176+
key=lambda p: (
177+
ipaddress.ip_network(p, strict=False).version,
178+
ipaddress.ip_network(p, strict=False),
179+
p)):
180+
# Filter by address family
181+
try:
182+
net = ipaddress.ip_network(prefix, strict=False)
183+
except ValueError:
184+
continue
185+
186+
if af == "ipv4" and net.version != 4:
187+
continue
188+
if af == "ipv6" and net.version != 6:
189+
continue
190+
191+
entry = cfg_table[prefix]
192+
193+
# Determine state from state DB
194+
state_entry = state_table.get(prefix, {})
195+
state = state_entry.get("state", "N/A")
196+
state = state.capitalize() if state != "N/A" else state
197+
198+
# Build option flags
199+
flags = []
200+
if entry.get("as-set", "false") == "true":
201+
flags.append("A")
202+
if entry.get("bbr-required", "false") == "true":
203+
flags.append("B")
204+
if entry.get("summary-only", "false") == "true":
205+
flags.append("S")
206+
flags_str = ",".join(flags) if flags else ""
207+
208+
agg_prefix_list = entry.get("aggregate-address-prefix-list", "")
209+
contrib_prefix_list = entry.get("contributing-address-prefix-list", "")
210+
211+
body.append([prefix, state, flags_str, agg_prefix_list, contrib_prefix_list])
212+
213+
click.echo("Flags: A - As Set, B - BBR Required, S - Summary Only\n")
214+
click.echo(tabulate.tabulate(body, header))

show/bgp_frr_v4.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from sonic_py_common import multi_asic, device_info
66
from show.main import ip
7+
from show import bgp_cli
78
import utilities_common.bgp_util as bgp_util
89
import utilities_common.cli as clicommon
910
import utilities_common.constants as constants
@@ -83,6 +84,14 @@ def network(ipaddress, info_type, namespace):
8384
network_helper(ipaddress, info_type, namespace)
8485

8586

87+
# 'aggregate-address' subcommand ("show ip bgp aggregate-address")
88+
@bgp.command('aggregate-address')
89+
@clicommon.pass_db
90+
def aggregate_address(db):
91+
"""Show IPv4 BGP aggregate addresses"""
92+
bgp_cli.show_aggregate_address(db, "ipv4")
93+
94+
8695
@bgp.group(cls=clicommon.AliasedGroup)
8796
@click.argument('vrf', required=True)
8897
@click.pass_context

show/bgp_frr_v6.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from sonic_py_common import multi_asic
44
import utilities_common.cli as clicommon
55
from show.main import ipv6
6+
from show import bgp_cli
67
import utilities_common.multi_asic as multi_asic_util
78
import utilities_common.bgp_util as bgp_util
89
import utilities_common.constants as constants
@@ -72,6 +73,14 @@ def network(ipaddress, info_type, namespace):
7273
network_helper(ipaddress, info_type, namespace)
7374

7475

76+
# 'aggregate-address' subcommand ("show ipv6 bgp aggregate-address")
77+
@bgp.command('aggregate-address')
78+
@clicommon.pass_db
79+
def aggregate_address(db):
80+
"""Show IPv6 BGP aggregate addresses"""
81+
bgp_cli.show_aggregate_address(db, "ipv6")
82+
83+
7584
@bgp.group(cls=clicommon.AliasedGroup)
7685
@click.argument('vrf', required=True)
7786
@click.pass_context

0 commit comments

Comments
 (0)