-
Notifications
You must be signed in to change notification settings - Fork 91
Add External API endpoints for BGP unnumbered interface manager status #10705
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| // This Source Code Form is subject to the terms of the Mozilla Public | ||
| // License, v. 2.0. If a copy of the MPL was not distributed with this | ||
| // file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
|
|
||
| use nexus_db_queries::context::OpContext; | ||
| use nexus_types::external_api::networking::{ | ||
| SwitchUnnumberedInterface, SwitchUnnumberedManagerState, | ||
| }; | ||
| use omicron_common::api::external::Error; | ||
| use sled_agent_types::early_networking::SwitchSlot; | ||
| use strum::IntoEnumIterator; | ||
|
|
||
| fn maghemite_interface_name(interface_name: &str) -> String { | ||
| format!("tfport{interface_name}_0") | ||
| } | ||
|
|
||
| impl super::Nexus { | ||
| pub async fn bgp_unnumbered_manager_status( | ||
| &self, | ||
| _opctx: &OpContext, | ||
| ) -> Result<Vec<SwitchUnnumberedManagerState>, Error> { | ||
| // Ask each switch about the BGP unnumbered interfaces it manages. | ||
| let mg_clients = self.mg_clients().await.map_err(|err| { | ||
| Error::internal_error(&format!("failed to get mg clients: {err}")) | ||
| })?; | ||
| let mut result = Vec::new(); | ||
| for switch_slot in SwitchSlot::iter() { | ||
| // Log an error if we only have one scrimlet, but keep going. | ||
| // We still want to return anything we're able to collect. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're not guaranteed to have any clients here; from the docs on As written, that means if we get back an empty map from { "switch0": "no client found: blah blah error", "switch1": "SomeValidStatus" } |
||
| let Some(mg_client) = mg_clients.get(&switch_slot) else { | ||
| warn!( | ||
| self.log, "no mgd client found for switch slot"; | ||
| "switch-slot" => ?switch_slot, | ||
| ); | ||
| continue; | ||
| }; | ||
| let status = mg_client | ||
| .get_bgp_unnumbered_manager_state() | ||
| .await | ||
| .map_err(|e| { | ||
| Error::internal_error(&format!( | ||
| "maghemite get BGP unnumbered manager state: {e}" | ||
| )) | ||
| })? | ||
| .into_inner(); | ||
|
|
||
| result.push(SwitchUnnumberedManagerState { | ||
| switch_slot, | ||
| state: status.into(), | ||
| }); | ||
| } | ||
| Ok(result) | ||
| } | ||
|
|
||
| pub async fn bgp_unnumbered_interfaces( | ||
| &self, | ||
| _opctx: &OpContext, | ||
| ) -> Result<Vec<SwitchUnnumberedInterface>, Error> { | ||
| // Ask each switch about the BGP unnumbered interfaces it manages. | ||
| let mg_clients = self.mg_clients().await.map_err(|err| { | ||
| Error::internal_error(&format!("failed to get mg clients: {err}")) | ||
| })?; | ||
| let mut result = Vec::new(); | ||
| for switch_slot in SwitchSlot::iter() { | ||
| // Log an error if we only have one scrimlet, but keep going. | ||
| // We still want to return anything we're able to collect. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same note here about maybe not having clients, and maybe returning a map keyed by switch slot so we can give back partial success?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I initially thought about returning a map keyed on switch slot, but it looked like other endpoints for switch-specific queries (e.g. lldp) all used the approach of returning a vec of wrapper structs that have switch slot as a member. If we do go with a map, I think we could get rid of the wrapper structs I don't work in this codebase very often, so I'm happy to go whichever way is preferable to the folks who live here |
||
| let Some(mg_client) = mg_clients.get(&switch_slot) else { | ||
| warn!( | ||
| self.log, "no mgd client found for switch slot"; | ||
| "switch-slot" => ?switch_slot, | ||
| ); | ||
| continue; | ||
| }; | ||
| let interfaces = mg_client | ||
| .get_bgp_unnumbered_interfaces() | ||
| .await | ||
| .map_err(|e| { | ||
| Error::internal_error(&format!( | ||
| "maghemite get BGP unnumbered interfaces: {e}" | ||
| )) | ||
| })? | ||
| .into_inner(); | ||
|
|
||
| for interface in interfaces { | ||
| result.push(SwitchUnnumberedInterface { | ||
| switch_slot, | ||
| interface: interface.into(), | ||
| }); | ||
| } | ||
| } | ||
| Ok(result) | ||
| } | ||
|
|
||
| pub async fn bgp_unnumbered_interface( | ||
| &self, | ||
| _opctx: &OpContext, | ||
| switch_slot: SwitchSlot, | ||
| interface_name: String, | ||
| ) -> Result<SwitchUnnumberedInterface, Error> { | ||
| let mg_clients = self.mg_clients().await.map_err(|err| { | ||
| Error::internal_error(&format!("failed to get mg clients: {err}")) | ||
| })?; | ||
| let mg_client = mg_clients.get(&switch_slot).ok_or_else(|| { | ||
| Error::internal_error(&format!( | ||
| "no mgd client found for switch slot {switch_slot:?}" | ||
| )) | ||
| })?; | ||
|
|
||
| let interface_name = maghemite_interface_name(&interface_name); | ||
| let interface = mg_client | ||
| .get_bgp_unnumbered_interface_detail(&interface_name) | ||
| .await | ||
| .map_err(|e| { | ||
| Error::internal_error(&format!( | ||
| "maghemite get BGP unnumbered interface detail: {e}" | ||
| )) | ||
| })? | ||
| .into_inner(); | ||
|
|
||
| Ok(SwitchUnnumberedInterface { | ||
| switch_slot, | ||
| interface: interface.into(), | ||
| }) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that we have a great pattern here, but these stand out in being the only ones to nest something under
bgp. Maybe all of them should look like/bgp/*, but for now, for consistency maybe it should be/v1/system/networking/bgp-unnumbered-interfaces?omicron/nexus/external-api/output/nexus_tags.txt
Lines 272 to 289 in 5e1011e
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can shift over to that convention for consistency. Thanks!