diff --git a/common/src/api/external/mod.rs b/common/src/api/external/mod.rs index 60325218f1c..f04c01713f1 100644 --- a/common/src/api/external/mod.rs +++ b/common/src/api/external/mod.rs @@ -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, } @@ -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, @@ -2487,13 +2487,6 @@ pub struct SwitchPortSettingsView { /// Layer 2 link settings. pub links: Vec, - /// Link-layer discovery protocol (LLDP) settings. - pub link_lldp: Vec, - - /// TX equalization settings. These are optional, and most links will not - /// need them. - pub tx_eq: Vec>, - /// Layer 3 interface settings. pub interfaces: Vec, @@ -2507,7 +2500,7 @@ pub struct SwitchPortSettingsView { pub bgp_peers: Vec, /// Layer 3 IP address settings. - pub addresses: Vec, + pub addresses: Vec, } /// This structure maps a port settings object to a port settings groups. Port @@ -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, - - /// The tx_eq configuration id for this link. - pub tx_eq_config_id: Option, - /// The name of this link. pub link_name: String, @@ -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, + + /// The tx_eq configuration for this link. + pub tx_eq_config: Option, } /// A link layer discovery protocol (LLDP) service configuration. @@ -2745,18 +2738,6 @@ pub struct TxEqConfig { pub post1: Option, } -impl From 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")] @@ -2824,7 +2805,7 @@ pub struct SwitchPortRouteConfig { pub dst: oxnet::IpNet, /// The route's gateway address. - pub gw: oxnet::IpNet, + pub gw: IpAddr, /// The VLAN identifier for the route. Use this if the gateway is reachable /// over an 802.1Q tagged L2 segment. @@ -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 { + /// 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, + + /// 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")] diff --git a/nexus/db-model/src/schema.rs b/nexus/db-model/src/schema.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/nexus/db-model/src/schema.rs @@ -0,0 +1 @@ + diff --git a/nexus/db-model/src/switch_port.rs b/nexus/db-model/src/switch_port.rs index 0bca70dddfa..af79fd10735 100644 --- a/nexus/db-model/src/switch_port.rs +++ b/nexus/db-model/src/switch_port.rs @@ -286,9 +286,9 @@ impl SwitchPortSettings { } } -impl Into for SwitchPortSettings { - fn into(self) -> external::SwitchPortSettings { - external::SwitchPortSettings { identity: self.identity() } +impl Into for SwitchPortSettings { + fn into(self) -> external::SwitchPortSettingsIdentity { + external::SwitchPortSettingsIdentity { identity: self.identity() } } } @@ -407,21 +407,6 @@ impl SwitchPortLinkConfig { } } -impl Into 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, @@ -625,7 +610,7 @@ impl Into 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), } diff --git a/nexus/db-queries/src/db/datastore/switch_port.rs b/nexus/db-queries/src/db/datastore/switch_port.rs index b892a03f7b8..bc47df04e8b 100644 --- a/nexus/db-queries/src/db/datastore/switch_port.rs +++ b/nexus/db-queries/src/db/datastore/switch_port.rs @@ -12,11 +12,11 @@ use crate::db::datastore::address_lot::{ ReserveBlockError, ReserveBlockTxnError, }; use crate::db::model::{ - LldpLinkConfig, Name, SwitchInterfaceConfig, SwitchPort, - SwitchPortAddressConfig, SwitchPortBgpPeerConfig, SwitchPortConfig, - SwitchPortLinkConfig, SwitchPortRouteConfig, SwitchPortSettings, - SwitchPortSettingsGroup, SwitchPortSettingsGroups, - SwitchVlanInterfaceConfig, TxEqConfig, + LldpLinkConfig, Name, SwitchInterfaceConfig, SwitchLinkFec, + SwitchLinkSpeed, SwitchPort, SwitchPortAddressConfig, + SwitchPortBgpPeerConfig, SwitchPortConfig, SwitchPortLinkConfig, + SwitchPortRouteConfig, SwitchPortSettings, SwitchPortSettingsGroup, + SwitchPortSettingsGroups, SwitchVlanInterfaceConfig, TxEqConfig, }; use crate::db::pagination::paginated; use async_bb8_diesel::{AsyncRunQueryDsl, Connection}; @@ -30,15 +30,17 @@ use nexus_db_errors::ErrorHandler; use nexus_db_errors::OptionalError; use nexus_db_errors::public_error_from_diesel; use nexus_db_model::{ - BgpConfig, SqlU8, SqlU16, SqlU32, SwitchPortBgpPeerConfigAllowExport, - SwitchPortBgpPeerConfigAllowImport, SwitchPortBgpPeerConfigCommunity, + AddressLot, BgpConfig, SqlU8, SqlU16, SqlU32, + SwitchPortBgpPeerConfigAllowExport, SwitchPortBgpPeerConfigAllowImport, + SwitchPortBgpPeerConfigCommunity, }; use nexus_types::external_api::params; +use nexus_types::identity::Resource; use omicron_common::api::external::http_pagination::PaginatedBy; use omicron_common::api::external::{ self, CreateResult, DataPageParams, DeleteResult, Error, ImportExportPolicy, ListResultVec, LookupResult, NameOrId, ResourceType, - UpdateResult, + SwitchPortAddressView, UpdateResult, }; use ref_cast::RefCast; use serde::{Deserialize, Serialize}; @@ -99,14 +101,14 @@ pub struct SwitchPortSettingsCombinedResult { pub settings: SwitchPortSettings, pub groups: Vec, pub port: SwitchPortConfig, - pub links: Vec, + pub links: Vec, pub link_lldp: Vec, - pub tx_eq: Vec>, + pub tx_eq: Vec, pub interfaces: Vec, pub vlan_interfaces: Vec, pub routes: Vec, pub bgp_peers: Vec, - pub addresses: Vec, + pub addresses: Vec, } impl SwitchPortSettingsCombinedResult { @@ -127,21 +129,13 @@ impl SwitchPortSettingsCombinedResult { } } -impl Into - for SwitchPortSettingsCombinedResult -{ - fn into(self) -> external::SwitchPortSettingsView { - external::SwitchPortSettingsView { - settings: self.settings.into(), +impl Into for SwitchPortSettingsCombinedResult { + fn into(self) -> external::SwitchPortSettings { + external::SwitchPortSettings { + identity: self.settings.identity(), port: self.port.into(), groups: self.groups.into_iter().map(Into::into).collect(), links: self.links.into_iter().map(Into::into).collect(), - link_lldp: self.link_lldp.into_iter().map(Into::into).collect(), - tx_eq: self - .tx_eq - .into_iter() - .map(|t| if let Some(t) = t { Some(t.into()) } else { None }) - .collect(), interfaces: self.interfaces.into_iter().map(Into::into).collect(), vlan_interfaces: self .vlan_interfaces @@ -150,7 +144,7 @@ impl Into .collect(), routes: self.routes.into_iter().map(Into::into).collect(), bgp_peers: self.bgp_peers.into_iter().map(Into::into).collect(), - addresses: self.addresses.into_iter().map(Into::into).collect(), + addresses: self.addresses, } } } @@ -161,6 +155,33 @@ pub struct SwitchPortSettingsGroupCreateResult { pub settings: SwitchPortSettingsCombinedResult, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct LinkConfigCombinedResult { + pub port_settings_id: Uuid, + pub link_name: String, + pub mtu: SqlU16, + pub fec: Option, + pub speed: SwitchLinkSpeed, + pub autoneg: bool, + pub lldp_link_config: Option, + pub tx_eq_config: Option, +} + +impl From for external::SwitchPortLinkConfig { + fn from(value: LinkConfigCombinedResult) -> Self { + Self { + port_settings_id: value.port_settings_id, + link_name: value.link_name, + mtu: *value.mtu, + fec: value.fec.map(Into::into), + speed: value.speed.into(), + autoneg: value.autoneg, + lldp_link_config: value.lldp_link_config.map(Into::into), + tx_eq_config: value.tx_eq_config.map(Into::into), + } + } +} + impl DataStore { pub async fn switch_port_settings_exist( &self, @@ -468,14 +489,13 @@ impl DataStore { self as link_config, dsl as link_config_dsl, }; - result.links = link_config_dsl::switch_port_settings_link_config + let link_configs = link_config_dsl::switch_port_settings_link_config .filter(link_config::port_settings_id.eq(id)) .select(SwitchPortLinkConfig::as_select()) .load_async::(&conn) .await?; - let lldp_link_ids: Vec = result - .links + let lldp_link_ids: Vec = link_configs .iter() .filter_map(|link| link.lldp_link_config_id) .collect(); @@ -488,14 +508,14 @@ impl DataStore { .load_async::(&conn) .await?; - let tx_eq_ids_and_nulls :Vec>= result - .links + let tx_eq_ids_and_nulls :Vec>= link_configs .iter() - .map(|link| link.tx_eq_config_id) + .map(|link| link.tx_eq_config_id ) .collect(); + let tx_eq_ids: Vec = tx_eq_ids_and_nulls .iter() - .cloned() + .cloned() .flatten() .collect(); @@ -506,12 +526,43 @@ impl DataStore { .limit(1) .load_async::(&conn) .await?; - result.tx_eq = tx_eq_ids_and_nulls.iter().map(|x| - if let Some(id) = x { - configs.iter().find(|c| c.id == *id).cloned() - } else { - None - }).collect(); + result.tx_eq = tx_eq_ids_and_nulls + .iter() + .filter_map(|x| + if let Some(id) = x { + configs.iter().find(|c| c.id == *id).cloned()} + else { + None + }) + .collect(); + + result.links = link_configs + .into_iter() + .map(|config| { + let lldp_link_config = match config.lldp_link_config_id { + Some(id) => result.link_lldp.iter().find(|c| c.id == id), + None => None, + } + .cloned(); + + let tx_eq_config = match config.tx_eq_config_id { + Some(id) => configs.iter().find(|c| c.id == id), + None => None, + } + .cloned(); + + LinkConfigCombinedResult { + port_settings_id: config.port_settings_id, + link_name: config.link_name, + mtu: config.mtu, + fec: config.fec, + speed: config.speed, + autoneg: config.autoneg, + lldp_link_config, + tx_eq_config, + } + }) + .collect(); // get the interface configs use nexus_db_schema::schema::switch_port_settings_interface_config::{ @@ -640,13 +691,14 @@ impl DataStore { self as address_config, dsl as address_config_dsl, }; - result.addresses = - address_config_dsl::switch_port_settings_address_config + let addresses = address_config_dsl::switch_port_settings_address_config .filter(address_config::port_settings_id.eq(id)) .select(SwitchPortAddressConfig::as_select()) .load_async::(&conn) .await?; + result.addresses = switch_port_address_view(&conn, addresses).await?; + Ok(result) } }) @@ -1211,7 +1263,7 @@ async fn do_switch_port_settings_create( let mut link_config = Vec::with_capacity(params.links.len()); let mut tx_eq_config = Vec::with_capacity(params.links.len()); - for (link_name, c) in ¶ms.links { + for c in ¶ms.links { let lldp_link_config = LldpLinkConfig::new( c.lldp.enabled, c.lldp.link_name.clone(), @@ -1229,19 +1281,16 @@ async fn do_switch_port_settings_create( let config = TxEqConfig::new(t.pre1, t.pre2, t.main, t.post2, t.post1); let tx_eq_config_id = config.id; - tx_eq_config.push(Some(config)); + tx_eq_config.push(config); Some(tx_eq_config_id) } - _ => { - tx_eq_config.push(None); - None - } + _ => None, }; link_config.push(SwitchPortLinkConfig::new( psid, lldp_config_id, - link_name.clone(), + c.link_name.to_string(), c.mtu, c.fec.map(|fec| fec.into()), c.speed.into(), @@ -1256,29 +1305,54 @@ async fn do_switch_port_settings_create( .get_results_async(conn) .await?; - // We want to insert the Some(config) values into the table, but preserve the - // full vector of None/Some values. - let v: Vec = tx_eq_config.iter().flatten().cloned().collect(); let _ = diesel::insert_into(tx_eq_config_dsl::tx_eq_config) - .values(v) + .values(tx_eq_config.clone()) .returning(TxEqConfig::as_returning()) .get_results_async(conn) .await?; result.tx_eq = tx_eq_config; - result.links = + let link_configs = diesel::insert_into(link_config_dsl::switch_port_settings_link_config) .values(link_config) .returning(SwitchPortLinkConfig::as_returning()) .get_results_async(conn) .await?; + result.links = link_configs + .into_iter() + .map(|config| { + let lldp_link_config = match config.lldp_link_config_id { + Some(id) => result.link_lldp.iter().find(|c| c.id == id), + None => None, + } + .cloned(); + + let tx_eq_config = match config.tx_eq_config_id { + Some(id) => result.tx_eq.iter().find(|c| c.id == id), + None => None, + } + .cloned(); + + LinkConfigCombinedResult { + port_settings_id: config.port_settings_id, + link_name: config.link_name, + mtu: config.mtu, + fec: config.fec, + speed: config.speed, + autoneg: config.autoneg, + lldp_link_config, + tx_eq_config, + } + }) + .collect(); + let mut interface_config = Vec::with_capacity(params.interfaces.len()); let mut vlan_interface_config = Vec::new(); - for (interface_name, i) in ¶ms.interfaces { + for i in ¶ms.interfaces { let ifx_config = SwitchInterfaceConfig::new( psid, - interface_name.clone(), + i.link_name.to_string(), i.v6_enabled, i.kind.into(), ); @@ -1306,11 +1380,11 @@ async fn do_switch_port_settings_create( let mut route_config = Vec::with_capacity(params.routes.len()); - for (interface_name, r) in ¶ms.routes { + for r in ¶ms.routes { for route in &r.routes { route_config.push(SwitchPortRouteConfig::new( psid, - interface_name.clone(), + r.link_name.to_string(), route.dst.into(), route.gw.into(), route.vid.map(Into::into), @@ -1330,7 +1404,7 @@ async fn do_switch_port_settings_create( BTreeMap::new(); let mut bgp_peer_config = Vec::new(); - for (interface_name, peer_config) in ¶ms.bgp_peers { + for peer_config in ¶ms.bgp_peers { for p in &peer_config.peers { peer_by_addr.insert(p.addr, &p); use nexus_db_schema::schema::bgp_config; @@ -1373,7 +1447,7 @@ async fn do_switch_port_settings_create( .into_iter() .map(|x| SwitchPortBgpPeerConfigAllowImport { port_settings_id: id, - interface_name: interface_name.clone(), + interface_name: peer_config.link_name.to_string(), addr: p.addr.into(), prefix: x.into(), }) @@ -1392,7 +1466,7 @@ async fn do_switch_port_settings_create( .into_iter() .map(|x| SwitchPortBgpPeerConfigAllowExport { port_settings_id: id, - interface_name: interface_name.clone(), + interface_name: peer_config.link_name.to_string(), addr: p.addr.into(), prefix: x.into(), }) @@ -1412,7 +1486,7 @@ async fn do_switch_port_settings_create( .into_iter() .map(|x| SwitchPortBgpPeerConfigCommunity { port_settings_id: id, - interface_name: interface_name.clone(), + interface_name: peer_config.link_name.to_string(), addr: p.addr.into(), community: x.into(), }) @@ -1427,7 +1501,7 @@ async fn do_switch_port_settings_create( bgp_peer_config.push(SwitchPortBgpPeerConfig::new( psid, bgp_config_id, - interface_name.clone(), + peer_config.link_name.to_string(), p, )); } @@ -1479,7 +1553,7 @@ async fn do_switch_port_settings_create( let mut address_config = Vec::new(); use nexus_db_schema::schema::address_lot; - for (interface_name, a) in ¶ms.addresses { + for a in ¶ms.addresses { for address in &a.addresses { let address_lot_id = match &address.address_lot { NameOrId::Id(id) => address_lot::table @@ -1536,12 +1610,12 @@ async fn do_switch_port_settings_create( block.id, rsvd_block.id, address.address.into(), - interface_name.clone(), + a.link_name.to_string(), address.vlan_id, )); } } - result.addresses = diesel::insert_into( + let addresses = diesel::insert_into( address_config_dsl::switch_port_settings_address_config, ) .values(address_config) @@ -1549,6 +1623,42 @@ async fn do_switch_port_settings_create( .get_results_async(conn) .await?; + result.addresses = switch_port_address_view(conn, addresses).await?; + + Ok(result) +} + +async fn switch_port_address_view( + conn: &Connection>, + addresses: Vec, +) -> Result, diesel::result::Error> { + use nexus_db_schema::schema::{address_lot, address_lot_block}; + + let mut result = vec![]; + + for address in addresses { + let lot = address_lot::table + .inner_join( + address_lot_block::table + .on(address_lot_block::address_lot_id.eq(address_lot::id)), + ) + .filter(address_lot_block::id.eq(address.address_lot_block_id)) + .select(AddressLot::as_select()) + .limit(1) + .first_async::(conn) + .await?; + + result.push(SwitchPortAddressView { + port_settings_id: address.port_settings_id, + address_lot_id: lot.id(), + address_lot_name: lot.name().clone(), + address_lot_block_id: address.address_lot_block_id, + address: address.address.into(), + vlan_id: address.vlan_id.map(Into::into), + interface_name: address.interface_name, + }) + } + Ok(result) } @@ -1752,7 +1862,7 @@ mod test { NameOrId, }; use omicron_test_utils::dev; - use std::collections::HashMap; + use std::str::FromStr; use uuid::Uuid; #[tokio::test] @@ -1806,37 +1916,36 @@ mod test { geometry: SwitchPortGeometry::Qsfp28x1, }, groups: Vec::new(), - links: HashMap::new(), - interfaces: HashMap::new(), - routes: HashMap::new(), - bgp_peers: HashMap::from([( - "phy0".into(), - BgpPeerConfig { - peers: vec![BgpPeer { - bgp_config: NameOrId::Name( - "test-bgp-config".parse().unwrap(), - ), - interface_name: "qsfp0".into(), - addr: "192.168.1.1".parse().unwrap(), - hold_time: 0, - idle_hold_time: 0, - delay_open: 0, - connect_retry: 0, - keepalive: 0, - remote_asn: None, - min_ttl: None, - md5_auth_key: None, - multi_exit_discriminator: None, - communities: Vec::new(), - local_pref: None, - enforce_first_as: false, - allowed_export: ImportExportPolicy::NoFiltering, - allowed_import: ImportExportPolicy::NoFiltering, - vlan_id: None, - }], - }, - )]), - addresses: HashMap::new(), + links: vec![], + interfaces: vec![], + routes: vec![], + bgp_peers: vec![BgpPeerConfig { + link_name: Name::from_str("phy0") + .expect("phy0 should be a valid link name"), + peers: vec![BgpPeer { + bgp_config: NameOrId::Name( + "test-bgp-config".parse().unwrap(), + ), + interface_name: "qsfp0".into(), + addr: "192.168.1.1".parse().unwrap(), + hold_time: 0, + idle_hold_time: 0, + delay_open: 0, + connect_retry: 0, + keepalive: 0, + remote_asn: None, + min_ttl: None, + md5_auth_key: None, + multi_exit_discriminator: None, + communities: Vec::new(), + local_pref: None, + enforce_first_as: false, + allowed_export: ImportExportPolicy::NoFiltering, + allowed_import: ImportExportPolicy::NoFiltering, + vlan_id: None, + }], + }], + addresses: vec![], }; let settings_result = datastore diff --git a/nexus/db-schema/src/schema.rs b/nexus/db-schema/src/schema.rs index e1614e671f0..17daaabdf7b 100644 --- a/nexus/db-schema/src/schema.rs +++ b/nexus/db-schema/src/schema.rs @@ -2175,6 +2175,12 @@ allow_tables_to_appear_in_same_query!( bgp_config ); +allow_tables_to_appear_in_same_query!( + address_lot, + address_lot_block, + switch_port_settings, +); + allow_tables_to_appear_in_same_query!(disk, virtual_provisioning_resource); allow_tables_to_appear_in_same_query!(volume, virtual_provisioning_resource); diff --git a/nexus/external-api/src/lib.rs b/nexus/external-api/src/lib.rs index e647dfabed6..eb05586146a 100644 --- a/nexus/external-api/src/lib.rs +++ b/nexus/external-api/src/lib.rs @@ -1669,7 +1669,7 @@ pub trait NexusExternalApi { async fn networking_switch_port_settings_create( rqctx: RequestContext, new_settings: TypedBody, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// Delete switch port settings #[endpoint { @@ -1693,7 +1693,10 @@ pub trait NexusExternalApi { query_params: Query< PaginatedByNameOrId, >, - ) -> Result>, HttpError>; + ) -> Result< + HttpResponseOk>, + HttpError, + >; /// Get information about switch port #[endpoint { @@ -1704,7 +1707,7 @@ pub trait NexusExternalApi { async fn networking_switch_port_settings_view( rqctx: RequestContext, path_params: Path, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// List switch ports #[endpoint { diff --git a/nexus/src/app/background/tasks/networking.rs b/nexus/src/app/background/tasks/networking.rs index 5cf7d737b53..ff5ae94431c 100644 --- a/nexus/src/app/background/tasks/networking.rs +++ b/nexus/src/app/background/tasks/networking.rs @@ -63,7 +63,7 @@ pub(crate) fn api_to_dpd_port_settings( //TODO breakouts let link_id = LinkId(0); - let tx_eq = if let Some(Some(t)) = settings.tx_eq.get(0) { + let tx_eq = if let Some(t) = settings.tx_eq.get(0) { Some(TxEq { pre1: t.pre1, pre2: t.pre2, @@ -104,7 +104,7 @@ pub(crate) fn api_to_dpd_port_settings( addrs: settings .addresses .iter() - .map(|a| a.address.ip()) + .map(|a| a.address.addr()) .collect(), }, ); diff --git a/nexus/src/app/background/tasks/sync_switch_configuration.rs b/nexus/src/app/background/tasks/sync_switch_configuration.rs index 1bffd2f0c28..93cca88e4b2 100644 --- a/nexus/src/app/background/tasks/sync_switch_configuration.rs +++ b/nexus/src/app/background/tasks/sync_switch_configuration.rs @@ -930,24 +930,24 @@ impl BackgroundTask for SwitchPortSettingsManager { }, }; - // TODO https://github.com/oxidecomputer/omicron/issues/3062 - let tx_eq = if let Some(Some(c)) = info.tx_eq.get(0) { - Some(TxEqConfig { - pre1: c.pre1, - pre2: c.pre2, - main: c.main, - post2: c.post2, - post1: c.post1, - }) - } else { - None - }; + // TODO https://github.com/oxidecomputer/omicron/issues/3062 + let tx_eq = if let Some(c) = info.tx_eq.get(0) { + Some(TxEqConfig { + pre1: c.pre1, + pre2: c.pre2, + main: c.main, + post2: c.post2, + post1: c.post1, + }) + } else { + None + }; let mut port_config = PortConfigV2 { addresses: info.addresses.iter().map(|a| UplinkAddressConfig { - address: a.address.into(), - vlan_id: a.vlan_id.map(|v| v.into()) + address: a.address, + vlan_id: a.vlan_id }).collect(), autoneg: info .links @@ -1556,7 +1556,7 @@ fn uplinks( }) }; - let tx_eq = if let Some(Some(c)) = config.tx_eq.get(0) { + let tx_eq = if let Some(c) = config.tx_eq.get(0) { Some(TxEqConfig { pre1: c.pre1, pre2: c.pre2, @@ -1574,8 +1574,8 @@ fn uplinks( .addresses .iter() .map(|a| UplinkAddressConfig { - address: a.address.into(), - vlan_id: a.vlan_id.map(|v| v.into()), + address: a.address, + vlan_id: a.vlan_id, }) .collect(), lldp, diff --git a/nexus/src/app/rack.rs b/nexus/src/app/rack.rs index 3db5e46f967..a2ef92810ee 100644 --- a/nexus/src/app/rack.rs +++ b/nexus/src/app/rack.rs @@ -569,11 +569,11 @@ impl super::Nexus { identity, port_config, groups: vec![], - links: HashMap::new(), - interfaces: HashMap::new(), - routes: HashMap::new(), - bgp_peers: HashMap::new(), - addresses: HashMap::new(), + links: vec![], + interfaces: vec![], + routes: vec![], + bgp_peers: vec![], + addresses: vec![], }; let addresses: Vec
= uplink_config @@ -586,9 +586,13 @@ impl super::Nexus { }) .collect(); - port_settings_params - .addresses - .insert("phy0".to_string(), AddressConfig { addresses }); + let link_name = + Name::from_str("phy0").expect("interface name should be valid"); + + port_settings_params.addresses.push(AddressConfig { + link_name: link_name.clone(), + addresses, + }); let routes: Vec = uplink_config .routes @@ -603,7 +607,7 @@ impl super::Nexus { port_settings_params .routes - .insert("phy0".to_string(), RouteConfig { routes }); + .push(RouteConfig { link_name: link_name.clone(), routes }); let peers: Vec = uplink_config .bgp_peers @@ -612,7 +616,7 @@ impl super::Nexus { bgp_config: NameOrId::Name( format!("as{}", r.asn).parse().unwrap(), ), - interface_name: "phy0".into(), + interface_name: link_name.to_string(), addr: r.addr.into(), hold_time: r.hold_time() as u32, idle_hold_time: r.idle_hold_time() as u32, @@ -634,7 +638,7 @@ impl super::Nexus { port_settings_params .bgp_peers - .insert("phy0".to_string(), BgpPeerConfig { peers }); + .push(BgpPeerConfig { link_name: link_name.clone(), peers }); let lldp = match &uplink_config.lldp { None => LldpLinkConfigCreate { @@ -656,6 +660,7 @@ impl super::Nexus { }; let link = LinkConfigCreate { + link_name: link_name.clone(), //TODO https://github.com/oxidecomputer/omicron/issues/2274 mtu: 1500, fec: uplink_config.uplink_port_fec.map(|fec| fec.into()), @@ -665,7 +670,7 @@ impl super::Nexus { tx_eq: uplink_config.tx_eq.map(|t| t.into()), }; - port_settings_params.links.insert("phy".to_string(), link); + port_settings_params.links.push(link); match self .db_datastore diff --git a/nexus/src/app/switch_port.rs b/nexus/src/app/switch_port.rs index 129397f8269..2c470306b20 100644 --- a/nexus/src/app/switch_port.rs +++ b/nexus/src/app/switch_port.rs @@ -59,7 +59,7 @@ impl super::Nexus { fn switch_port_settings_validate( params: ¶ms::SwitchPortSettingsCreate, ) -> CreateResult<()> { - for x in params.bgp_peers.values() { + for x in ¶ms.bgp_peers { for p in x.peers.iter() { if let Some(ref key) = p.md5_auth_key { if key.len() > 80 { diff --git a/nexus/src/external_api/http_entrypoints.rs b/nexus/src/external_api/http_entrypoints.rs index ec62cffcd7b..f42afa4c3b8 100644 --- a/nexus/src/external_api/http_entrypoints.rs +++ b/nexus/src/external_api/http_entrypoints.rs @@ -80,7 +80,7 @@ use omicron_common::api::external::RouterRoute; use omicron_common::api::external::RouterRouteKind; use omicron_common::api::external::SwitchPort; use omicron_common::api::external::SwitchPortSettings; -use omicron_common::api::external::SwitchPortSettingsView; +use omicron_common::api::external::SwitchPortSettingsIdentity; use omicron_common::api::external::TufRepoGetResponse; use omicron_common::api::external::TufRepoInsertResponse; use omicron_common::api::external::VpcFirewallRuleUpdateParams; @@ -3589,7 +3589,7 @@ impl NexusExternalApi for NexusExternalApiImpl { async fn networking_switch_port_settings_create( rqctx: RequestContext, new_settings: TypedBody, - ) -> Result, HttpError> { + ) -> Result, HttpError> { let apictx = rqctx.context(); let handler = async { let nexus = &apictx.context.nexus; @@ -3599,7 +3599,7 @@ impl NexusExternalApi for NexusExternalApiImpl { let result = nexus.switch_port_settings_post(&opctx, params).await?; - let settings: SwitchPortSettingsView = result.into(); + let settings: SwitchPortSettings = result.into(); Ok(HttpResponseCreated(settings)) }; apictx @@ -3634,8 +3634,10 @@ impl NexusExternalApi for NexusExternalApiImpl { query_params: Query< PaginatedByNameOrId, >, - ) -> Result>, HttpError> - { + ) -> Result< + HttpResponseOk>, + HttpError, + > { let apictx = rqctx.context(); let handler = async { let nexus = &apictx.context.nexus; @@ -3668,7 +3670,7 @@ impl NexusExternalApi for NexusExternalApiImpl { async fn networking_switch_port_settings_view( rqctx: RequestContext, path_params: Path, - ) -> Result, HttpError> { + ) -> Result, HttpError> { let apictx = rqctx.context(); let handler = async { let nexus = &apictx.context.nexus; diff --git a/nexus/tests/integration_tests/switch_port.rs b/nexus/tests/integration_tests/switch_port.rs index f54ea64b328..967030f474e 100644 --- a/nexus/tests/integration_tests/switch_port.rs +++ b/nexus/tests/integration_tests/switch_port.rs @@ -4,6 +4,8 @@ //! Integration tests for operating on Ports +use std::str::FromStr; + use http::StatusCode; use http::method::Method; use nexus_test_utils::http_testing::{AuthnMode, NexusRequest, RequestBuilder}; @@ -16,11 +18,11 @@ use nexus_types::external_api::params::{ SwitchPortSettingsCreate, }; use nexus_types::external_api::views::Rack; -use omicron_common::api::external::ImportExportPolicy; use omicron_common::api::external::{ self, AddressLotKind, BgpPeer, IdentityMetadataCreateParams, LinkFec, - LinkSpeed, NameOrId, SwitchPort, SwitchPortSettingsView, + LinkSpeed, NameOrId, SwitchPort, SwitchPortSettings, }; +use omicron_common::api::external::{ImportExportPolicy, Name}; type ControlPlaneTestContext = nexus_test_utils::ControlPlaneTestContext; @@ -113,59 +115,55 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { name: "portofino".parse().unwrap(), description: "just a port".into(), }); + + let link_name = + Name::from_str("phy0").expect("phy0 should be a valid name"); + // links - settings.links.insert( - "phy0".into(), - LinkConfigCreate { - mtu: 4700, - lldp: LldpLinkConfigCreate { - enabled: true, - link_name: Some("Link Name".into()), - link_description: Some("link_ Dscription".into()), - chassis_id: Some("Chassis ID".into()), - system_name: Some("System Name".into()), - system_description: Some("System description".into()), - management_ip: None, - }, - fec: Some(LinkFec::None), - speed: LinkSpeed::Speed100G, - autoneg: false, - tx_eq: None, + settings.links.push(LinkConfigCreate { + link_name: link_name.clone(), + mtu: 4700, + lldp: LldpLinkConfigCreate { + enabled: true, + link_name: Some("Link Name".into()), + link_description: Some("link_ Dscription".into()), + chassis_id: Some("Chassis ID".into()), + system_name: Some("System Name".into()), + system_description: Some("System description".into()), + management_ip: None, }, - ); + fec: Some(LinkFec::None), + speed: LinkSpeed::Speed100G, + autoneg: false, + tx_eq: None, + }); // interfaces - settings.interfaces.insert( - "phy0".into(), - SwitchInterfaceConfigCreate { - v6_enabled: true, - kind: SwitchInterfaceKind::Primary, - }, - ); + settings.interfaces.push(SwitchInterfaceConfigCreate { + link_name: link_name.clone(), + v6_enabled: true, + kind: SwitchInterfaceKind::Primary, + }); // routes - settings.routes.insert( - "phy0".into(), - RouteConfig { - routes: vec![Route { - dst: "1.2.3.0/24".parse().unwrap(), - gw: "1.2.3.4".parse().unwrap(), - vid: None, - rib_priority: None, - }], - }, - ); + settings.routes.push(RouteConfig { + link_name: link_name.clone(), + routes: vec![Route { + dst: "1.2.3.0/24".parse().unwrap(), + gw: "1.2.3.4".parse().unwrap(), + vid: None, + rib_priority: None, + }], + }); // addresses - settings.addresses.insert( - "phy0".into(), - AddressConfig { - addresses: vec![Address { - address: "203.0.113.10/24".parse().unwrap(), - vlan_id: None, - address_lot: NameOrId::Name("parkinglot".parse().unwrap()), - }], - }, - ); + settings.addresses.push(AddressConfig { + link_name: link_name.clone(), + addresses: vec![Address { + address: "203.0.113.10/24".parse().unwrap(), + vlan_id: None, + address_lot: NameOrId::Name("parkinglot".parse().unwrap()), + }], + }); - let created: SwitchPortSettingsView = NexusRequest::objects_post( + let created: SwitchPortSettings = NexusRequest::objects_post( client, "/v1/system/networking/switch-port-settings", &settings, @@ -185,7 +183,7 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { assert_eq!(&link0.link_name, "phy0"); assert_eq!(link0.mtu, 4700); - let lldp0 = &created.link_lldp[0]; + let lldp0 = link0.lldp_link_config.clone().unwrap(); assert_eq!(lldp0.enabled, true); assert_eq!(lldp0.link_name, Some("Link Name".to_string())); assert_eq!(lldp0.link_description, Some("Link Description".to_string())); @@ -204,13 +202,13 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { let route0 = &created.routes[0]; assert_eq!(route0.dst, "1.2.3.0/24".parse().unwrap()); - assert_eq!(route0.gw, "1.2.3.4".parse().unwrap()); + assert_eq!(&route0.gw.to_string(), "1.2.3.4"); let addr0 = &created.addresses[0]; assert_eq!(addr0.address, "203.0.113.10/24".parse().unwrap()); // Get the port settings back - let roundtrip: SwitchPortSettingsView = NexusRequest::object_get( + let roundtrip: SwitchPortSettings = NexusRequest::object_get( client, "/v1/system/networking/switch-port-settings/portofino", ) @@ -229,7 +227,7 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { assert_eq!(&link0.link_name, "phy0"); assert_eq!(link0.mtu, 4700); - let lldp0 = &roundtrip.link_lldp[0]; + let lldp0 = link0.lldp_link_config.clone().unwrap(); assert_eq!(lldp0.enabled, true); assert_eq!(lldp0.link_name, Some("Link Name".to_string())); assert_eq!(lldp0.link_description, Some("Link Description".to_string())); @@ -248,7 +246,7 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { let route0 = &roundtrip.routes[0]; assert_eq!(route0.dst, "1.2.3.0/24".parse().unwrap()); - assert_eq!(route0.gw, "1.2.3.4".parse().unwrap()); + assert_eq!(&route0.gw.to_string(), "1.2.3.4"); let addr0 = &roundtrip.addresses[0]; assert_eq!(addr0.address, "203.0.113.10/24".parse().unwrap()); @@ -267,7 +265,7 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { .unwrap(); // Create same port settings again. Should not see conflict. - let _created: SwitchPortSettingsView = NexusRequest::objects_post( + let _created: SwitchPortSettings = NexusRequest::objects_post( client, "/v1/system/networking/switch-port-settings", &settings, @@ -280,32 +278,30 @@ async fn test_port_settings_basic_crud(ctx: &ControlPlaneTestContext) { .unwrap(); // Update port settings. Should not see conflict. - settings.bgp_peers.insert( - "phy0".into(), - BgpPeerConfig { - peers: vec![BgpPeer { - bgp_config: NameOrId::Name("as47".parse().unwrap()), - interface_name: "phy0".to_string(), - addr: "1.2.3.4".parse().unwrap(), - hold_time: 6, - idle_hold_time: 6, - delay_open: 0, - connect_retry: 3, - keepalive: 2, - remote_asn: None, - min_ttl: None, - md5_auth_key: None, - multi_exit_discriminator: None, - communities: Vec::new(), - local_pref: None, - enforce_first_as: false, - allowed_export: ImportExportPolicy::NoFiltering, - allowed_import: ImportExportPolicy::NoFiltering, - vlan_id: None, - }], - }, - ); - let _created: SwitchPortSettingsView = NexusRequest::objects_post( + settings.bgp_peers.push(BgpPeerConfig { + link_name: link_name.clone(), + peers: vec![BgpPeer { + bgp_config: NameOrId::Name("as47".parse().unwrap()), + interface_name: "phy0".to_string(), + addr: "1.2.3.4".parse().unwrap(), + hold_time: 6, + idle_hold_time: 6, + delay_open: 0, + connect_retry: 3, + keepalive: 2, + remote_asn: None, + min_ttl: None, + md5_auth_key: None, + multi_exit_discriminator: None, + communities: Vec::new(), + local_pref: None, + enforce_first_as: false, + allowed_export: ImportExportPolicy::NoFiltering, + allowed_import: ImportExportPolicy::NoFiltering, + vlan_id: None, + }], + }); + let _created: SwitchPortSettings = NexusRequest::objects_post( client, "/v1/system/networking/switch-port-settings", &settings, diff --git a/nexus/types/src/external_api/params.rs b/nexus/types/src/external_api/params.rs index 337b335e68d..78965057ee8 100644 --- a/nexus/types/src/external_api/params.rs +++ b/nexus/types/src/external_api/params.rs @@ -14,7 +14,7 @@ use omicron_common::api::external::{ ByteCount, FailureDomain, Hostname, IdentityMetadataCreateParams, IdentityMetadataUpdateParams, InstanceAutoRestartPolicy, InstanceCpuCount, LinkFec, LinkSpeed, Name, NameOrId, PaginationOrder, RouteDestination, - RouteTarget, TxEqConfig, UserId, + RouteTarget, UserId, }; use omicron_common::disk::DiskVariant; use oxnet::{IpNet, Ipv4Net, Ipv6Net}; @@ -27,7 +27,6 @@ use serde::{ }; use std::collections::BTreeMap; use std::collections::BTreeSet; -use std::collections::HashMap; use std::{net::IpAddr, str::FromStr}; use url::Url; use uuid::Uuid; @@ -1740,20 +1739,31 @@ pub struct SwtichPortSettingsGroupCreate { pub struct SwitchPortSettingsCreate { #[serde(flatten)] pub identity: IdentityMetadataCreateParams, + pub port_config: SwitchPortConfigCreate, + + #[serde(default)] pub groups: Vec, + /// Links indexed by phy name. On ports that are not broken out, this is /// always phy0. On a 2x breakout the options are phy0 and phy1, on 4x /// phy0-phy3, etc. - pub links: HashMap, + pub links: Vec, + /// Interfaces indexed by link name. - pub interfaces: HashMap, + #[serde(default)] + pub interfaces: Vec, + /// Routes indexed by interface name. - pub routes: HashMap, + #[serde(default)] + pub routes: Vec, + /// BGP peers indexed by interface name. - pub bgp_peers: HashMap, + #[serde(default)] + pub bgp_peers: Vec, + /// Addresses indexed by interface name. - pub addresses: HashMap, + pub addresses: Vec, } impl SwitchPortSettingsCreate { @@ -1764,11 +1774,11 @@ impl SwitchPortSettingsCreate { geometry: SwitchPortGeometry::Qsfp28x1, }, groups: Vec::new(), - links: HashMap::new(), - interfaces: HashMap::new(), - routes: HashMap::new(), - bgp_peers: HashMap::new(), - addresses: HashMap::new(), + links: Vec::new(), + interfaces: Vec::new(), + routes: Vec::new(), + bgp_peers: Vec::new(), + addresses: Vec::new(), } } } @@ -1798,6 +1808,9 @@ pub enum SwitchPortGeometry { /// Switch link configuration. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct LinkConfigCreate { + /// Link name + pub link_name: Name, + /// Maximum transmission unit for the link. pub mtu: u16, @@ -1819,6 +1832,36 @@ pub struct LinkConfigCreate { pub tx_eq: Option, } +/// Per-port tx-eq overrides. This can be used to fine-tune the transceiver +/// equalization settings to improve signal integrity. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, PartialEq)] +pub struct TxEqConfig { + /// Pre-cursor tap1 + pub pre1: Option, + /// Pre-cursor tap2 + pub pre2: Option, + /// Main tap + pub main: Option, + /// Post-cursor tap2 + pub post2: Option, + /// Post-cursor tap1 + pub post1: Option, +} + +impl From for TxEqConfig { + fn from( + x: omicron_common::api::internal::shared::TxEqConfig, + ) -> TxEqConfig { + TxEqConfig { + pre1: x.pre1, + pre2: x.pre2, + main: x.main, + post2: x.post2, + post1: x.post1, + } + } +} + /// The LLDP configuration associated with a port. #[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)] pub struct LldpLinkConfigCreate { @@ -1848,6 +1891,9 @@ pub struct LldpLinkConfigCreate { /// address will be created for the interface. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct SwitchInterfaceConfigCreate { + /// Link the interface will be assigned to + pub link_name: Name, + /// Whether or not IPv6 is enabled. pub v6_enabled: bool, @@ -1886,6 +1932,9 @@ pub struct SwitchVlanInterface { /// Route configuration data associated with a switch port configuration. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct RouteConfig { + /// Link the route should be active on + pub link_name: Name, + /// The set of routes assigned to a switch port. pub routes: Vec, } @@ -1916,6 +1965,9 @@ pub struct BgpConfigSelector { #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct BgpPeerConfig { + /// Link that the peer is reachable on + pub link_name: Name, + pub peers: Vec, } @@ -2031,6 +2083,9 @@ pub struct BfdSessionDisable { /// A set of addresses associated with a port configuration. #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct AddressConfig { + /// Link to assign the address to + pub link_name: Name, + /// The set of addresses assigned to the port configuration. pub addresses: Vec
, } diff --git a/openapi/nexus.json b/openapi/nexus.json index e4027a51291..45108f69bf0 100644 --- a/openapi/nexus.json +++ b/openapi/nexus.json @@ -9389,7 +9389,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SwitchPortSettingsResultsPage" + "$ref": "#/components/schemas/SwitchPortSettingsIdentityResultsPage" } } } @@ -9427,7 +9427,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SwitchPortSettingsView" + "$ref": "#/components/schemas/SwitchPortSettings" } } } @@ -9493,7 +9493,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SwitchPortSettingsView" + "$ref": "#/components/schemas/SwitchPortSettings" } } } @@ -12784,10 +12784,19 @@ "items": { "$ref": "#/components/schemas/Address" } + }, + "link_name": { + "description": "Link to assign the address to", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] } }, "required": [ - "addresses" + "addresses", + "link_name" ] }, "AddressLot": { @@ -14118,6 +14127,14 @@ "BgpPeerConfig": { "type": "object", "properties": { + "link_name": { + "description": "Link that the peer is reachable on", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, "peers": { "type": "array", "items": { @@ -14126,6 +14143,7 @@ } }, "required": [ + "link_name", "peers" ] }, @@ -20055,6 +20073,14 @@ } ] }, + "link_name": { + "description": "Link name", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, "lldp": { "description": "The link-layer discovery protocol (LLDP) configuration for the link.", "allOf": [ @@ -20089,6 +20115,7 @@ }, "required": [ "autoneg", + "link_name", "lldp", "mtu", "speed" @@ -21478,6 +21505,14 @@ "description": "Route configuration data associated with a switch port configuration.", "type": "object", "properties": { + "link_name": { + "description": "Link the route should be active on", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, "routes": { "description": "The set of routes assigned to a switch port.", "type": "array", @@ -21487,6 +21522,7 @@ } }, "required": [ + "link_name", "routes" ] }, @@ -23215,6 +23251,14 @@ } ] }, + "link_name": { + "description": "Link the interface will be assigned to", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, "v6_enabled": { "description": "Whether or not IPv6 is enabled.", "type": "boolean" @@ -23222,6 +23266,7 @@ }, "required": [ "kind", + "link_name", "v6_enabled" ] }, @@ -23364,7 +23409,7 @@ "switch_location" ] }, - "SwitchPortAddressConfig": { + "SwitchPortAddressView": { "description": "An IP address configuration for a port settings object.", "type": "object", "properties": { @@ -23381,6 +23426,19 @@ "type": "string", "format": "uuid" }, + "address_lot_id": { + "description": "The id of the address lot this address is drawn from.", + "type": "string", + "format": "uuid" + }, + "address_lot_name": { + "description": "The name of the address lot this address is drawn from.", + "allOf": [ + { + "$ref": "#/components/schemas/Name" + } + ] + }, "interface_name": { "description": "The interface name this address belongs to.", "type": "string" @@ -23401,6 +23459,8 @@ "required": [ "address", "address_lot_block_id", + "address_lot_id", + "address_lot_name", "interface_name", "port_settings_id" ] @@ -23535,11 +23595,14 @@ "description": "The name of this link.", "type": "string" }, - "lldp_link_config_id": { + "lldp_link_config": { "nullable": true, - "description": "The link-layer discovery protocol service configuration id for this link.", - "type": "string", - "format": "uuid" + "description": "The link-layer discovery protocol service configuration for this link.", + "allOf": [ + { + "$ref": "#/components/schemas/LldpLinkConfig" + } + ] }, "mtu": { "description": "The maximum transmission unit for this link.", @@ -23560,11 +23623,14 @@ } ] }, - "tx_eq_config_id": { + "tx_eq_config": { "nullable": true, - "description": "The tx_eq configuration id for this link.", - "type": "string", - "format": "uuid" + "description": "The tx_eq configuration for this link.", + "allOf": [ + { + "$ref": "#/components/schemas/TxEqConfig2" + } + ] } }, "required": [ @@ -23610,11 +23676,8 @@ }, "gw": { "description": "The route's gateway address.", - "allOf": [ - { - "$ref": "#/components/schemas/IpNet" - } - ] + "type": "string", + "format": "ip" }, "interface_name": { "description": "The interface name this route configuration is assigned to.", @@ -23648,18 +23711,53 @@ ] }, "SwitchPortSettings": { - "description": "A switch port settings identity whose id may be used to view additional details.", + "description": "This structure contains all port settings information in one place. It's a convenience data structure for getting a complete view of a particular port's settings.", "type": "object", "properties": { + "addresses": { + "description": "Layer 3 IP address settings.", + "type": "array", + "items": { + "$ref": "#/components/schemas/SwitchPortAddressView" + } + }, + "bgp_peers": { + "description": "BGP peer settings.", + "type": "array", + "items": { + "$ref": "#/components/schemas/BgpPeer" + } + }, "description": { "description": "human-readable free-form text about a resource", "type": "string" }, + "groups": { + "description": "Switch port settings included from other switch port settings groups.", + "type": "array", + "items": { + "$ref": "#/components/schemas/SwitchPortSettingsGroups" + } + }, "id": { "description": "unique, immutable, system-controlled identifier for each resource", "type": "string", "format": "uuid" }, + "interfaces": { + "description": "Layer 3 interface settings.", + "type": "array", + "items": { + "$ref": "#/components/schemas/SwitchInterfaceConfig" + } + }, + "links": { + "description": "Layer 2 link settings.", + "type": "array", + "items": { + "$ref": "#/components/schemas/SwitchPortLinkConfig" + } + }, "name": { "description": "unique, mutable, user-controlled identifier for each resource", "allOf": [ @@ -23668,6 +23766,21 @@ } ] }, + "port": { + "description": "Layer 1 physical port settings.", + "allOf": [ + { + "$ref": "#/components/schemas/SwitchPortConfig" + } + ] + }, + "routes": { + "description": "IP route settings.", + "type": "array", + "items": { + "$ref": "#/components/schemas/SwitchPortRouteConfig" + } + }, "time_created": { "description": "timestamp when this resource was created", "type": "string", @@ -23677,14 +23790,29 @@ "description": "timestamp when this resource was last modified", "type": "string", "format": "date-time" + }, + "vlan_interfaces": { + "description": "Vlan interface settings.", + "type": "array", + "items": { + "$ref": "#/components/schemas/SwitchVlanInterfaceConfig" + } } }, "required": [ + "addresses", + "bgp_peers", "description", + "groups", "id", + "interfaces", + "links", "name", + "port", + "routes", "time_created", - "time_modified" + "time_modified", + "vlan_interfaces" ] }, "SwitchPortSettingsCreate": { @@ -23693,15 +23821,16 @@ "properties": { "addresses": { "description": "Addresses indexed by interface name.", - "type": "object", - "additionalProperties": { + "type": "array", + "items": { "$ref": "#/components/schemas/AddressConfig" } }, "bgp_peers": { "description": "BGP peers indexed by interface name.", - "type": "object", - "additionalProperties": { + "default": [], + "type": "array", + "items": { "$ref": "#/components/schemas/BgpPeerConfig" } }, @@ -23709,6 +23838,7 @@ "type": "string" }, "groups": { + "default": [], "type": "array", "items": { "$ref": "#/components/schemas/NameOrId" @@ -23716,15 +23846,16 @@ }, "interfaces": { "description": "Interfaces indexed by link name.", - "type": "object", - "additionalProperties": { + "default": [], + "type": "array", + "items": { "$ref": "#/components/schemas/SwitchInterfaceConfigCreate" } }, "links": { "description": "Links indexed by phy name. On ports that are not broken out, this is always phy0. On a 2x breakout the options are phy0 and phy1, on 4x phy0-phy3, etc.", - "type": "object", - "additionalProperties": { + "type": "array", + "items": { "$ref": "#/components/schemas/LinkConfigCreate" } }, @@ -23736,22 +23867,19 @@ }, "routes": { "description": "Routes indexed by interface name.", - "type": "object", - "additionalProperties": { + "default": [], + "type": "array", + "items": { "$ref": "#/components/schemas/RouteConfig" } } }, "required": [ "addresses", - "bgp_peers", "description", - "groups", - "interfaces", "links", "name", - "port_config", - "routes" + "port_config" ] }, "SwitchPortSettingsGroups": { @@ -23774,128 +23902,65 @@ "port_settings_id" ] }, - "SwitchPortSettingsResultsPage": { - "description": "A single page of results", + "SwitchPortSettingsIdentity": { + "description": "A switch port settings identity whose id may be used to view additional details.", "type": "object", "properties": { - "items": { - "description": "list of items on this page of results", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchPortSettings" - } - }, - "next_page": { - "nullable": true, - "description": "token used to fetch the next page of results (if any)", + "description": { + "description": "human-readable free-form text about a resource", "type": "string" - } - }, - "required": [ - "items" - ] - }, - "SwitchPortSettingsView": { - "description": "This structure contains all port settings information in one place. It's a convenience data structure for getting a complete view of a particular port's settings.", - "type": "object", - "properties": { - "addresses": { - "description": "Layer 3 IP address settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchPortAddressConfig" - } - }, - "bgp_peers": { - "description": "BGP peer settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/BgpPeer" - } }, - "groups": { - "description": "Switch port settings included from other switch port settings groups.", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchPortSettingsGroups" - } - }, - "interfaces": { - "description": "Layer 3 interface settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchInterfaceConfig" - } - }, - "link_lldp": { - "description": "Link-layer discovery protocol (LLDP) settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/LldpLinkConfig" - } - }, - "links": { - "description": "Layer 2 link settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchPortLinkConfig" - } + "id": { + "description": "unique, immutable, system-controlled identifier for each resource", + "type": "string", + "format": "uuid" }, - "port": { - "description": "Layer 1 physical port settings.", + "name": { + "description": "unique, mutable, user-controlled identifier for each resource", "allOf": [ { - "$ref": "#/components/schemas/SwitchPortConfig" + "$ref": "#/components/schemas/Name" } ] }, - "routes": { - "description": "IP route settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchPortRouteConfig" - } - }, - "settings": { - "description": "The primary switch port settings handle.", - "allOf": [ - { - "$ref": "#/components/schemas/SwitchPortSettings" - } - ] + "time_created": { + "description": "timestamp when this resource was created", + "type": "string", + "format": "date-time" }, - "tx_eq": { - "description": "TX equalization settings. These are optional, and most links will not need them.", + "time_modified": { + "description": "timestamp when this resource was last modified", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "description", + "id", + "name", + "time_created", + "time_modified" + ] + }, + "SwitchPortSettingsIdentityResultsPage": { + "description": "A single page of results", + "type": "object", + "properties": { + "items": { + "description": "list of items on this page of results", "type": "array", "items": { - "nullable": true, - "allOf": [ - { - "$ref": "#/components/schemas/TxEqConfig" - } - ] + "$ref": "#/components/schemas/SwitchPortSettingsIdentity" } }, - "vlan_interfaces": { - "description": "Vlan interface settings.", - "type": "array", - "items": { - "$ref": "#/components/schemas/SwitchVlanInterfaceConfig" - } + "next_page": { + "nullable": true, + "description": "token used to fetch the next page of results (if any)", + "type": "string" } }, "required": [ - "addresses", - "bgp_peers", - "groups", - "interfaces", - "link_lldp", - "links", - "port", - "routes", - "settings", - "tx_eq", - "vlan_interfaces" + "items" ] }, "SwitchResultsPage": { @@ -24186,6 +24251,42 @@ } } }, + "TxEqConfig2": { + "description": "Per-port tx-eq overrides. This can be used to fine-tune the transceiver equalization settings to improve signal integrity.", + "type": "object", + "properties": { + "main": { + "nullable": true, + "description": "Main tap", + "type": "integer", + "format": "int32" + }, + "post1": { + "nullable": true, + "description": "Post-cursor tap1", + "type": "integer", + "format": "int32" + }, + "post2": { + "nullable": true, + "description": "Post-cursor tap2", + "type": "integer", + "format": "int32" + }, + "pre1": { + "nullable": true, + "description": "Pre-cursor tap1", + "type": "integer", + "format": "int32" + }, + "pre2": { + "nullable": true, + "description": "Pre-cursor tap2", + "type": "integer", + "format": "int32" + } + } + }, "TypedUuidForInstanceKind": { "type": "string", "format": "uuid"