Yank out type duplication for BGP peers - #803
Conversation
7d328c5 to
d986afd
Compare
This PR yanks out a ton of type duplication around BGP peer config.
We had a lot of this before, but BGP unnumbered added yet another split
for many of the types.
We started with 11 separate types:
1. Neighbor
2. UnnumberedNeighbor
3. BgpPeerConfig
4. UnnumberedBgpPeerConfig
5. BgpPeerParameters
6. BgpNeighborInfo
7. BgpUnnumberedNeighborInfo
8. BgpNeighborParameters
9. PeerConfig
10. SessionInfo
11. NeighborInfo
And ended with just 4 types:
1. NeighborConfig
2. Neighbor
3. SessionInfo
4. NeighborInfo
All in all, we end up with ~1500 fewer lines of code and a noticeably
streamlined order of operations for API handlers. The following summary
came from Claude since the full extent of the changes is so wide.
Added:
- New versioned API module mg-api-types-versions::v12
- (unify_bgp_neighbors/), exposed via latest.rs:
+ NeighborConfig: single write/input type. Peer identity plus
all session params inlined; no asn/group, no serde(flatten).
+ Neighbor: read/stored type via composition:
{ asn, group, config: NeighborConfig }.
+ NeighborGroupSelector { asn, group } for write endpoints.
+ Unified ApplyRequest (peers: map<group, Vec>)
and NeighborResetRequest.
+ v11<->v12 conversions: total From upgrades, partial TryFrom
(PeerKindMismatch) downgrades.
- API version (12, UNIFY_BGP_NEIGHBORS) in mg-api api_versions!.
- Regenerated OpenAPI doc mg-admin-12.0.0.
Removed:
- bgp::config::PeerConfig and its From impls (config.rs now holds
only RouterConfig).
- rdb storage types BgpNeighborInfo and BgpUnnumberedNeighborInfo
(and nested BgpNeighborParameters); the DB now persists Neighbor.
- External BgpPeerConfig/UnnumberedBgpPeerConfig and the
Neighbor/UnnumberedNeighbor split; the BgpPeerParameters struct.
- Conversion hand-copies Neighbor::from_bgp_peer_config and
from_rdb_neighbor_info.
- Router fork: new_unnumbered_session, ensure_unnumbered_session,
update_unnumbered_session; the peer_id param of
new_session_locked.
- UnnumberedManagerNdp::get_neighbor_session and its dead routers
field.
- Dead NeighborResetRequest::to_peer_id.
- Paired numbered/unnumbered mgd handlers (~10 collapsed to ~5).
- Retired the v11 OpenAPI doc to a .gitstub.
Changed:
- Identity unified on PeerId (Ip|Interface) with port: Option; asn/group
are request coordinates, not body fields.
- SessionInfo/NeighborInfo built From<&NeighborConfig>;
Error::UnknownPeer now carries a PeerId; update_session handles
both peer kinds.
- rdb db.rs: one PeerId-keyed tree; add/get/remove keyed by PeerId;
removed the unnumbered ops and BGP_UNNUMBERED_NEIGHBOR.
- mg-api: unified create/read/update/delete/clear as the required
VERSION_UNIFY_BGP_NEIGHBORS.. methods; older split endpoints kept
as version-bounded provided defaults. Create/update moved to
/bgp/config/neighbor-group/{asn}/{group} (Dropshot path-collision
fix).
- mgd: do_bgp_apply diff keyed by PeerId; dropped the
UNSPECIFIED:179 read-path fabrication; reworked add_neighbor,
add_neighbor_v1, get_exported_v1, and main.rs to the new shapes.
- mgadm: to_api_neighbor (was into_*) produces NeighborConfig;
create/update pass asn+group on the path.
- falcon-lab: builds and passes the nested Neighbor.
Fixed:
- deterministic_collision_resolution and jitter are now persisted;
previously hard-coded on rdb read and lost across reload.
Cleanups:
- Trimmed verbose compile-barrier comments to short notes
(mp_bgp, prefix_to_oxnet, static_admin, rdb/types).
- Removed mg-api-types doc-comment text that leaked internal
implementation into the OpenAPI spec.
- Scrubbed test.rs comments of references to removed types.
Fixes: #564
Signed-off-by: Trey Aspelund <trey@oxidecomputer.com>
d986afd to
498ce1b
Compare
|
marking this ready for review now. I still need to build out the omicron PR, but I don't think that should hold up any reviews on the maghemite side |
Signed-off-by: Trey Aspelund <trey@oxidecomputer.com>
1acc3c0 to
f4d484f
Compare
nicolaskagami
left a comment
There was a problem hiding this comment.
Nice! It's always great to see code being simplified away :)
I found a couple of things that are worth addressing down below and:
- The PR description mentions a
NeighborGroupSelectorwhich is nowhere to be found in this repo. - I like
PeerIdbut I think we could do more of it, such as withact_as_a_default_ipv6_router. I'm thinking about a better format overall.
Signed-off-by: Trey Aspelund <trey@oxidecomputer.com>
- validate session updates before mutating peer metadata; - retain a router when Apply drains all peers and require explicit Delete; - enforce one NeighborConfig per PeerId across groups with IdOrdMap; - return 404 for peer-kind mismatches in compatibility endpoints; - reject duplicate peers from legacy apply requests with HTTP 400; - make v13-to-v11 conversion exhaustive instead of dropping peers. Move group into NeighborConfig, retain the array-shaped wire and generated client representation, regenerate the v13 OpenAPI document, and add coverage for serialization, conversions, and apply/delete behavior. Signed-off-by: Trey Aspelund <trey@oxidecomputer.com>
Thanks! I am much happier with this shape than I was with the parallel un/numbered paths.
I fixed up the PR description (rather, I had claude do it because there are a lot of changes in this PR) with a summary of what's been done.
I understand the urge to move things like Maybe there's another shape we could pursue where |
That's not exactly what I was suggesting, exactly for the reason you give.
Yep. We could have |
There was a problem hiding this comment.
Thanks for the consolidation work here @taspelund. Comments follow.
Since the API surface change is large for this PR, I'd like to see an omicron branch that's working e2e in CI and in voxel before this lands.
There was a problem hiding this comment.
Given all the testing infrastructure we've built up with falcon lab and the amount of additional complexity abstracting connections over multiple underlying substrates costs, i'm not sure if its worth keeping the channel model and the indirection layer around.
I think there has also been some work recently on running tests based on multiple mgd daemons on the same host peering with each other over localhost addresses. That seems like it be more worthwhile than the channel-based tests.
| conn: &BgpConnectionTcp, | ||
| min_ttl: Option<u8>, | ||
| md5_key: Option<String>, | ||
| _listen_port: std::num::NonZeroU16, |
There was a problem hiding this comment.
remove leading _. You can use let _ = listen_port; in the linux stub.
| /// Address and port configuration for a session. | ||
| #[derive(Debug, Deserialize, Serialize, JsonSchema, Clone, PartialEq, Eq)] | ||
| pub struct SessionAddrInfo { | ||
| /// TCP port configured on the remote peer. `BGP_PORT` is used when the peer |
There was a problem hiding this comment.
What is meant by the second sentence in this comment? remote_port is not optional here. If this is about how a user of this structure fills this field in, I think that commentary should go in the using code and not here as this can very easily get out of sync.
| let name = lock!(neighbor.name).clone(); | ||
| let peer_group = lock!(neighbor.peer_group).clone(); | ||
| *lock!(self.neighbor.name) = name; | ||
| *lock!(self.neighbor.peer_group) = peer_group; |
There was a problem hiding this comment.
This can probably simplify to
*lock!(self.neighbor.name) = lock!(neighbor.name).clone();
*lock!(self.neighbor.peer_group) = lock!(neighbor.peer_group).clone();To reduce the chance of name and peer_group accidentally being held to long if code gets added after this later.
| impl Default for JitterRange { | ||
| fn default() -> Self { | ||
| Self { | ||
| min: 0.75, |
There was a problem hiding this comment.
These constants deserve some comments.
|
|
||
| // ===== upgrade conversions (v11 split -> v13 unified), total ===== | ||
|
|
||
| impl From<crate::v11::bgp::config::Neighbor> for Neighbor { |
There was a problem hiding this comment.
There is a LOT of version conversion going on here. It seems like we should account for this in our e2e tests like falcon lab by creating variants of our tests that drive things from clients that are one or even a few versions behind.
There was a problem hiding this comment.
I think this file can be removed?
There was a problem hiding this comment.
I think this file can be removed?
| Self::update_neighbor_v8(rqctx, request.map(Into::into)).await | ||
| } | ||
|
|
||
| // V1/V2 API - legacy Neighbor type with combined import/export policies. |
There was a problem hiding this comment.
At some point we can probably start pruning when it's guaranteed that things as far back as V1/V2 will never be in use?
| let (key, _) = item?; | ||
| tree.remove(key)?; | ||
| } | ||
| Ok(()) |
There was a problem hiding this comment.
I think we may need a tree.flush() here to ensure that all the removes took place by the time this function returns.
This PR removes the duplicated numbered/unnumbered BGP peer configuration paths introduced as BGP unnumbered support grew. The latest implementation goes from 11 overlapping API, storage, and session shapes to four core types:
NeighborConfigNeighborSessionInfoNeighborInfoOlder shapes remain only in versioned compatibility APIs. Overall, this removes more than 1,000 lines while making numbered and unnumbered peers follow the same configuration, persistence, and session-management paths.
API and type model
The new
UNIFY_BGP_NEIGHBORSAPI is version 13 and is exposed throughlatest.PeerIdremains solely the peer identity:Ipfor numbered peers orInterfacefor unnumbered peers.NeighborConfigcontains that identity,group, the optional TCPport, and all session parameters. The formerBgpPeerParameterslayer is inlined withoutserde(flatten).Neighboris the read/stored composition{ asn, config: NeighborConfig }./bgp/config/neighbor, with ASN carried byNeighbor.NeighborResetRequestand one handler.ApplyRequestis now an explicitly tagged operation:{"action":"apply","asn":47,"originate":[],"peers":[]} {"action":"delete","asn":47}Applyrepresents complete desired state for a router. An empty peer list retains the router, whileDeleteexplicitly removes the router and its state.Apply peers are represented internally as
IdOrdMap<NeighborConfig>, keyed only byPeerId, so one peer cannot appear in multiple groups. Serde and OpenAPI retain a normal JSON array representation, and Progenitor generatesVec<NeighborConfig>for clients. Duplicate peer identities are rejected when the request is deserialized.Internal simplification
bgp::config::PeerConfigand its conversion layer.BgpPeerConfig,UnnumberedBgpPeerConfig,BgpPeerParameters,BgpNeighborInfo,BgpUnnumberedNeighborInfo, andBgpNeighborParametersduplication.Neighbordirectly in one tree keyed by(ASN, PeerId)instead of maintaining separate numbered and unnumbered storage paths.PeerIdrather than parallel numbered/unnumbered implementations.Neighborshape used by the API and RDB.deterministic_collision_resolutionand timer jitter settings now survive persistence instead of being reconstructed with defaults.Compatibility and review fixes
PeerId::IpandPeerId::Interface, rather than silently dropping a peer kind. Explicit router deletion cannot be represented by v11 and returns a conversion error.Persistence note
The sled database is used for recovery from daemon crashes and restarts within a deployed zone. mgd upgrades replace and rebuild the zone, so no cross-version migration of the retired numbered/unnumbered neighbor tree is required.
Validation
cargo fmt --all --checkcargo test -p mg-api-types-versionsjust openapi-check mgdcargo check --manifest-path mg-admin-client/Cargo.tomlFixes: #564