Skip to content

[Nexthop][fboss2-dev] fboss2 config/delete protocol bgp peer-group commands#1395

Open
hillol-nexthop wants to merge 8 commits into
facebook:mainfrom
nexthop-ai:bgp-peer-group
Open

[Nexthop][fboss2-dev] fboss2 config/delete protocol bgp peer-group commands#1395
hillol-nexthop wants to merge 8 commits into
facebook:mainfrom
nexthop-ai:bgp-peer-group

Conversation

@hillol-nexthop

@hillol-nexthop hillol-nexthop commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Stacked on #1391 (config protocol bgp neighbor), which stacks on #1345 (config protocol bgp global) → #1344 (BGP-aware ConfigSession). Review/merge those first; this PR shows only the peer-group commands on top.

Summary

Collapses the ~20 legacy per-attribute config protocol bgp peer-group command classes (built on the deprecated folly::dynamic BgpConfigSession) into a single typed dispatcher, mirroring CmdConfigProtocolBgpNeighbor (#1391). The group name is the first positional token; the next one or two tokens name the attribute (matched longest-prefix-first, so timers hold-time wins over any timers prefix); the rest are its value(s). Handlers mutate the typed bgp::thrift::PeerGroup directly via ConfigSession::getBgpConfig() / saveBgpConfig(), so adding a tunable is a one-entry change in the dispatch table rather than a new command class.

Also adds delete protocol bgp peer-group <name>.

Covers the 24 attributes that have a PeerGroup thrift field: remote-asn/local-asn (4-byte-bounded), description, peer-tag, ingress-policy/egress-policy, rr-client, confed-peer, redistribute-peer, enhanced-route-refresh, connect-mode, add-path send|receive, afi disable-ipv4-afi|disable-ipv6-afi|ipv4-over-ipv6-nh, graceful-restart restart-time|stateful-ha, max-route pre-filter|post-filter (+ the warning-threshold/warning-only knobs), timers hold-time|keepalive|out-delay|withdraw-unprog-delay, and next-hop-self.

Behavior notes

  • Rejected instead of persisting dead config (precedent: cluster-id in the global PR, connect-mode BOTH in the neighbor PR): connect-mode BOTH (thrift only models is_passive). add-path send|receive merges into the AddPath enum bitmask (RECEIVE/SEND/BOTH), and clearing the last direction unsets the field.
  • Attributes with no per-peer-group thrift fieldafi ipv4-labeled-unicast, afi ipv6-labeled-unicast, peer-port — are not part of the dispatch table, so they are refused at parse time as unknown attributes; a rejected value never lands on disk.
  • CLI11 token stealing fixed (same fix as the neighbor command): positionals_at_end() stops parent-chain subcommand fallthrough from reclassifying an attribute token that matches a sibling command name once the group name has been consumed.
  • Integration tests verify the committed peer-group config through the daemon's getRunningConfig RPC — proving bgpd parsed and adopted the promoted peer_groups rather than only checking the file the CLI wrote.

Test Plan

UnitCmdConfigBgpPeerGroupTestFixture (12 tests: arg validation, longest-prefix match, add-path merge matrix, connect-mode, bool/string/route-limit attributes, value validation, unknown-attribute rejection) and CmdDeleteBgpPeerGroupTestFixture (4 tests). 16/16 pass.

Integration on device (real bgpd daemon), ConfigBgpPeerGroupTest: 11/11 pass, including commit-path tests that assert bgpd parses and adopts peer_groups via its getRunningConfig RPC.

Sample usage:

$ fboss2-dev config protocol bgp peer-group SPINE remote-asn 65510
Successfully set remote-asn to: 65510
$ fboss2-dev config protocol bgp peer-group SPINE timers hold-time 90
Successfully set timers hold-time to: 90 seconds
$ fboss2-dev config protocol bgp peer-group SPINE connect-mode BOTH
Error: connect-mode BOTH has no representation in bgp_config.thrift ...
$ fboss2-dev delete protocol bgp peer-group SPINE
Successfully deleted BGP peer-group SPINE

hillol-nexthop and others added 7 commits July 16, 2026 09:12
Make the fboss2 ConfigSession and `config session` lifecycle BGP-aware. This is
the base for the `config protocol bgp` commands, which stack on top.

- ConfigSession owns the BGP config as a typed bgp::thrift::BgpConfig, exactly
  the way it owns cfg::AgentConfig for the agent. getBgpConfig()/saveBgpConfig()
  expose the WHOLE typed config and stage it to ~/.fboss2/bgp_config.json.
  ConfigSession is BGP-aware but agnostic about the config's internal structure:
  it has no notion of "global" vs "peer" vs "peer-group" -- a command mutates
  whichever typed fields it needs, mirroring how interface commands mutate
  getAgentConfig().sw()->ports(). Because the whole config round-trips through
  the typed struct, no surgical merge / preserve-list is needed.
- commit() promotes bgp_config.json to /etc/coop/bgpcpp/bgpcpp.conf, restarts
  bgp_pp, and versions it in the same /etc/coop git repo as agent.conf.
- config session clear/diff/rebase/rollback are BGP-aware: clear removes a
  staged BGP session, diff shows staged BGP changes, rebase 3-way-merges
  bgpcpp.conf, rollback restores bgpcpp.conf + restarts bgp_pp (using metadata
  history so BGP-only commits are reachable).
- FbossServiceUtil + cli_metadata gain the bgp_pp (BGP_PP) service/action; the
  config lib gains the bgp_config cpp2 dependency.
- Unit tests in CmdConfigSession{,Diff}Test cover the typed round-trip, commit,
  rebase, and rollback behavior.

Test Plan:
- bazel test //fboss/cli/fboss2/test/config:cmd_config_test //fboss/cli/fboss2/test:cmd_test
- fboss2_integration_test on a DUT (ConfigConcurrentSessionsTest.ConflictAndRebase,
  ConfigSessionClearTest, ConfigInterfaceMtuTest, ConfigInterfaceDescriptionTest): 5/5 pass
The BGP++ daemon ships as bgpd.service (via the bgpd RPM) on devices and in
the fboss-sim runtime image; bgp_pp only ever worked via a manually created
alias unit. Rename the unit that ConfigSession/FbossServiceUtil restarts on
commit, and the related comments and unit-test expectations.
…ged bgpcpp/ subdir

`config session commit` writes the bgpd config to /etc/coop/bgpcpp/bgpcpp.conf
(kBgpGitRelPath) and expects the daemon to read exactly that path, which
forces a per-device systemd drop-in overriding bgpd's --config.

Mirror the agent.conf scheme instead: on every BGP commit, keep the
daemon-facing path /etc/coop/bgpcpp.conf a symlink into the CLI-managed
bgpcpp/ subdir (exactly as agent.conf symlinks to cli/agent.conf). bgpd keeps
its provisioned `--config /etc/coop/bgpcpp.conf` and follows the symlink, so
no drop-in is needed and the arrangement is self-healing across re-provision.
The symlink is git-tracked alongside the config so rollback restores it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add `fboss2-dev config protocol bgp global <attr> <value>` on top of the
BGP-aware ConfigSession (base PR). Edits the typed bgp::thrift::BgpConfig via
ConfigSession::getBgpConfig()/saveBgpConfig() -- the whole-config,
scope-agnostic typed API (no global/peer/peer-group special-casing in
ConfigSession).

- Collapse the 10 per-attribute global command classes into one dispatcher;
  reject cluster-id (no BgpConfig field) instead of writing dead config; bound
  switch-limit / max_golden_vips so out-of-range values aren't truncated.
- Integration tests: ConfigBgpGlobalTest (each attr set+commit, verified in the
  promoted /etc/coop/bgpcpp/bgpcpp.conf) and ConfigBgpSessionTest
  (clear/diff/commit/rollback + a no-op-restart regression), sharing
  ConfigBgpTestBase.

Test Plan:
- bazel test //fboss/cli/fboss2/test/config:cmd_config_test
- bazel build //fboss/cli/fboss2/test/integration_test:fboss2_integration_test
- fboss2_integration_test on a DUT with bgp_pp active: ConfigBgpSessionTest
  6/6 pass (clear, diff, commit-restarts-bgp_pp, rollback-restores-config, and
  the unchanged-config does-not-restart case); agent-session regression
  (ConfigInterfaceMtuTest) passes.
- Extract the value parsers (parseBool/parseInt/parseNonNegInt32, handler
  Result) into a shared BgpCliValueParsers.h so sibling BGP dispatchers can
  reuse them, and add a bounded parseAsn4Byte: local-asn/confed-asn accepted
  any uint64 and silently persisted out-of-range ASNs (>= 2^32 wrap the i64
  field negative).
- positionals_at_end() on the global command: CLI11's parent-chain subcommand
  fallthrough steals value tokens that match a sibling command name (e.g. a
  policy named "peer-group") and misparses the command.
- Integration test base: probe the bgpd unit, and pass
  -c safe.directory=/etc/coop on the raw git invocations (gitHead /
  bgpTrackedAtRevision), mirroring the CLI's Git class -- /etc/coop is owned
  by another user (e.g. coop) on provisioned devices, which git otherwise
  rejects as dubious ownership and the helpers silently return empty results.
The commit-path global-attribute tests asserted on the promoted
/etc/coop/bgpcpp/bgpcpp.conf, which only proves what the CLI wrote to disk.
Route ConfigBgpTestBase::setAndCommit() through a new
readRunningBgpConfigViaRpc() -- TBgpService::getRunningConfig against the local
bgpd -- so every positive ConfigBgpGlobalTest asserts its attribute in the
daemon's own view of its config, proving bgpd parsed and adopted the promoted
file after the commit-triggered restart.

The helper retries briefly on connection errors: systemd reports bgpd active as
soon as the process starts, but its thrift server binds the port a few seconds
later, so an RPC issued right after a restart races that window.

Test Plan:
- bazel build //fboss/cli/fboss2/test/integration_test:fboss2_integration_test
- fboss2_integration_test on a DUT with bgpd active: ConfigBgpGlobalTest 7/7
  pass (each attribute set+commit verified in bgpd's getRunningConfig view,
  plus the invalid-bool and negative-graceful-restart-time reject paths).
…or` commands

Adds `fboss2-dev config protocol bgp neighbor <ip> [<attribute> <value> ...]`
as a single dispatcher (mirroring the `global` command): the neighbor address
is the first positional token, one- or two-token attributes are matched
longest-prefix-first against a dispatch table, and handlers mutate the typed
bgp::thrift::BgpPeer via ConfigSession::getBgpConfig()/saveBgpConfig().

- Bare `neighbor <ip>` creates the peer; attribute sets find-or-create it,
  keyed by the folly::IPAddress-normalized peer_addr (prefix notation for
  passive sessions is accepted).
- New peers are seeded with parseable any-addresses for local_addr /
  next_hop4 / next_hop6: bgpd constructs folly::IPAddress from these
  non-optional fields at config load and aborts on the empty strings a
  freshly created peer would otherwise serialize (reproduced on an NH-4010-F
  device).
- add-path send/receive merge into the single AddPath enum (RECEIVE/SEND/
  BOTH); clearing the last direction unsets the field.
- Rejected instead of persisting dead config: connect-mode BOTH (thrift only
  models is_passive), peer-port / bind-addr port (no per-peer port field),
  afi ipv4/ipv6-labeled-unicast (no thrift field).
- Value validation: 4-byte ASN range, IP family for next-hop4/next-hop6,
  bind-addr address parse, thrift-enum membership for advertise-lbw /
  receive-lbw, non-negative timers and route limits. A rejected value never
  lands on disk.
- Deletes the 26 per-attribute `peer` command classes plus the now-dead
  per-peer folly::dynamic BgpConfigSession API they used; parity attributes
  those commands supported (next-hop4/6, next-hop-self, peer-id, type,
  link-bandwidth, advertise/receive-lbw, timers withdraw-unprog-delay,
  pre/post warning limits) are carried over in the dispatcher.
- `delete protocol bgp neighbor <ip>` removes the matching peer from the
  staged config via shared BgpPeerAddr.h normalize/find helpers; deleting an
  unknown neighbor errors without persisting anything.
- bgp_json_to_cli.py peer generators emit the new grammar (shlex-escaped,
  including the neighbor address); explicit zero timers/limits are no longer
  dropped and symbolic add_path enum names are handled.
- Integration tests verify committed neighbor config through the bgpd
  TBgpService::getRunningConfig RPC (with a brief connection retry: systemd
  reports the unit active before the thrift server binds its port), proving
  the daemon parsed and adopted the promoted config rather than only
  checking the file the CLI wrote.
@hillol-nexthop
hillol-nexthop requested review from a team as code owners July 20, 2026 07:01
@meta-cla meta-cla Bot added the CLA Signed label Jul 20, 2026
Collapse the ~20 legacy per-attribute `config protocol bgp peer-group`
command classes (on the deprecated folly::dynamic BgpConfigSession) into a
single typed dispatcher, mirroring CmdConfigProtocolBgpNeighbor: the group
name is the first positional, the next one or two tokens name the attribute,
and the rest are its value(s); dispatch and per-attribute parsing live in the
.cpp so adding a peer-group tunable is a one-entry change. Attributes write
the typed bgp::thrift::PeerGroup directly via ConfigSession::getBgpConfig()/
saveBgpConfig().

Covers the 24 attributes with a PeerGroup thrift field (remote/local-asn,
description, peer-tag, ingress/egress-policy, rr-client, confed-peer,
redistribute-peer, enhanced-route-refresh, connect-mode, add-path send|
receive, afi disable-ipv4/ipv6-afi|ipv4-over-ipv6-nh, graceful-restart
restart-time|stateful-ha, max-route pre/post-filter + warning knobs, timers
hold-time|keepalive|out-delay|withdraw-unprog-delay, next-hop-self).
Attributes with no per-peer-group thrift field (afi ipv4/ipv6-labeled-unicast,
peer-port) are not part of the dispatch table, so they are refused as unknown
attributes rather than persisting dead config. connect-mode BOTH is rejected
(no is_passive representation); add-path merges the RECEIVE/SEND bitmask.

Adds `delete protocol bgp peer-group <name>`. Unit tests
(CmdConfigBgpPeerGroupTest, CmdDeleteBgpPeerGroupTest) and integration tests
(ConfigBgpPeerGroupTest) verify staging, the reject paths, and — for the
commit path — that bgpd parses and adopts peer_groups via its
getRunningConfig RPC.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant