Skip to content

Improve usability of switch port settings #7966

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 40 additions & 32 deletions common/src/api/external/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2465,7 +2465,7 @@ pub struct SwitchPort {
#[derive(
ObjectIdentity, Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq,
)]
pub struct SwitchPortSettings {
pub struct SwitchPortSettingsIdentity {
#[serde(flatten)]
pub identity: IdentityMetadata,
}
Expand All @@ -2474,9 +2474,9 @@ pub struct SwitchPortSettings {
/// convenience data structure for getting a complete view of a particular
/// port's settings.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)]
pub struct SwitchPortSettingsView {
/// The primary switch port settings handle.
pub settings: SwitchPortSettings,
pub struct SwitchPortSettings {
#[serde(flatten)]
pub identity: IdentityMetadata,

/// Switch port settings included from other switch port settings groups.
pub groups: Vec<SwitchPortSettingsGroups>,
Expand All @@ -2487,13 +2487,6 @@ pub struct SwitchPortSettingsView {
/// Layer 2 link settings.
pub links: Vec<SwitchPortLinkConfig>,

/// Link-layer discovery protocol (LLDP) settings.
pub link_lldp: Vec<LldpLinkConfig>,

/// TX equalization settings. These are optional, and most links will not
/// need them.
pub tx_eq: Vec<Option<TxEqConfig>>,

/// Layer 3 interface settings.
pub interfaces: Vec<SwitchInterfaceConfig>,

Expand All @@ -2507,7 +2500,7 @@ pub struct SwitchPortSettingsView {
pub bgp_peers: Vec<BgpPeer>,

/// Layer 3 IP address settings.
pub addresses: Vec<SwitchPortAddressConfig>,
pub addresses: Vec<SwitchPortAddressView>,
}

/// This structure maps a port settings object to a port settings groups. Port
Expand Down Expand Up @@ -2632,13 +2625,6 @@ pub struct SwitchPortLinkConfig {
/// The port settings this link configuration belongs to.
pub port_settings_id: Uuid,

/// The link-layer discovery protocol service configuration id for this
/// link.
pub lldp_link_config_id: Option<Uuid>,

/// The tx_eq configuration id for this link.
pub tx_eq_config_id: Option<Uuid>,

/// The name of this link.
pub link_name: String,

Expand All @@ -2655,6 +2641,13 @@ pub struct SwitchPortLinkConfig {

/// Whether or not the link has autonegotiation enabled.
pub autoneg: bool,

/// The link-layer discovery protocol service configuration for this
/// link.
pub lldp_link_config: Option<LldpLinkConfig>,

/// The tx_eq configuration for this link.
pub tx_eq_config: Option<TxEqConfig>,
}

/// A link layer discovery protocol (LLDP) service configuration.
Expand Down Expand Up @@ -2745,18 +2738,6 @@ pub struct TxEqConfig {
pub post1: Option<i32>,
}

impl From<crate::api::internal::shared::TxEqConfig> for TxEqConfig {
fn from(x: crate::api::internal::shared::TxEqConfig) -> TxEqConfig {
TxEqConfig {
pre1: x.pre1,
pre2: x.pre2,
main: x.main,
post2: x.post2,
post1: x.post1,
}
}
}

/// Describes the kind of an switch interface.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)]
#[serde(rename_all = "snake_case")]
Expand Down Expand Up @@ -2824,7 +2805,7 @@ pub struct SwitchPortRouteConfig {
pub dst: oxnet::IpNet,

/// The route's gateway address.
pub gw: oxnet::IpNet,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why can this go from IpNet to IpAddr?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was likely never supposed to be an IpNet. The user provides an IpAddr when they create the configuration:

The DB stores singular ipv4 addresses as a full CIDR with /32, so that's probably how we ended up returning an IpNet instead of an IpAddr like what the user supplied.

pub gw: IpAddr,

/// The VLAN identifier for the route. Use this if the gateway is reachable
/// over an 802.1Q tagged L2 segment.
Expand Down Expand Up @@ -2980,6 +2961,33 @@ pub struct SwitchPortAddressConfig {
pub interface_name: String,
}

/// An IP address configuration for a port settings object.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)]
pub struct SwitchPortAddressView {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this matches up with SwitchPortSettingsView, but we don't usually put View on the end of these structs we call views. In this case I think SwitchPortAddress might be ok, though it makes it sound like it's just the address. But either nothing or something more descriptive than View would be an improvement.

SwitchPortSettingsView can't be changed to SwitchPortSettings because there is already a SwitchPortSettings, and SwitchPortSettingsConfig is ridiculous. What if instead of the settings key, you took that #[serde(flatten)] identity bit and stuck it directly in SwitchPortSettingsView? Then you could rename the struct SwitchPortSettings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Giving this a try now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the existing SwitchPortSettings struct that wraps the IdentityMetadata is what we return when we list the SwitchPortSettings resource. I'm not sure on the original motivation for this, but the complete SwitchPortSettingsView struct is quite large and requires a bit of work to populate, so maybe we didn't want to do that when listing the resource.

Would SwitchPortSettingsIdentity make more sense for the wrapper struct? That would allow us to use SwitchPortSettings for the "view" struct.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. That's a naming conundrum. I don't think we distinguish the full view from the list item anywhere else, so we are inventing the convention here. *Identity feels natural because we have structs in the Rust code that are named like that, though we don't have any existing view schemas in the API ending with Identity. I think your proposal is solid: the list endpoint would return ResultsPage<SwitchPortSettingsIdentity> and the detail would be SwitchPortSettings. An alternative pair of names could be SwitchPortSettings and SwitchPortSettingsDetail. But I think I like the first one better.

Yet another option would be to return the full thing for the list as well, but you weren't kidding about it being a lot, so we probably shouldn't do that.

@ahl what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated these type names to SwitchPortSettings and SwitchPortSettingsIdentity.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, I think it looks good.

/// The port settings object this address configuration belongs to.
pub port_settings_id: Uuid,

/// The id of the address lot this address is drawn from.
pub address_lot_id: Uuid,

/// The name of the address lot this address is drawn from.
pub address_lot_name: Name,

/// The id of the address lot block this address is drawn from.
pub address_lot_block_id: Uuid,

/// The IP address and prefix.
pub address: oxnet::IpNet,

/// An optional VLAN ID
pub vlan_id: Option<u16>,

/// The interface name this address belongs to.
// TODO: https://github.com/oxidecomputer/omicron/issues/3050
// Use `Name` instead of `String` for `interface_name` type
pub interface_name: String,
}

/// The current state of a BGP peer.
#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)]
#[serde(rename_all = "snake_case")]
Expand Down
1 change: 1 addition & 0 deletions nexus/db-model/src/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mistakenly added file?

23 changes: 4 additions & 19 deletions nexus/db-model/src/switch_port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,9 +286,9 @@ impl SwitchPortSettings {
}
}

impl Into<external::SwitchPortSettings> for SwitchPortSettings {
fn into(self) -> external::SwitchPortSettings {
external::SwitchPortSettings { identity: self.identity() }
impl Into<external::SwitchPortSettingsIdentity> for SwitchPortSettings {
fn into(self) -> external::SwitchPortSettingsIdentity {
external::SwitchPortSettingsIdentity { identity: self.identity() }
}
}

Expand Down Expand Up @@ -407,21 +407,6 @@ impl SwitchPortLinkConfig {
}
}

impl Into<external::SwitchPortLinkConfig> for SwitchPortLinkConfig {
fn into(self) -> external::SwitchPortLinkConfig {
external::SwitchPortLinkConfig {
port_settings_id: self.port_settings_id,
lldp_link_config_id: self.lldp_link_config_id,
tx_eq_config_id: self.tx_eq_config_id,
link_name: self.link_name.clone(),
mtu: self.mtu.into(),
fec: self.fec.map(|fec| fec.into()),
speed: self.speed.into(),
autoneg: self.autoneg,
}
}
}

#[derive(
Queryable,
Insertable,
Expand Down Expand Up @@ -625,7 +610,7 @@ impl Into<external::SwitchPortRouteConfig> for SwitchPortRouteConfig {
port_settings_id: self.port_settings_id,
interface_name: self.interface_name.clone(),
dst: self.dst.into(),
gw: self.gw.into(),
gw: self.gw.ip(),
vlan_id: self.vid.map(Into::into),
rib_priority: self.rib_priority.map(Into::into),
}
Expand Down
Loading
Loading