Skip to content

Commit bcb4fc6

Browse files
[review] move mvlan and switch port uplinks (for mcast egress) out of pools
1 parent 6fe0c6a commit bcb4fc6

File tree

18 files changed

+15
-969
lines changed

18 files changed

+15
-969
lines changed

docs/control-plane-architecture.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ NOTE: Much of this material originally came from <<rfd48>> and <<rfd61>>. This
1414

1515
NOTE: The RFD references in this documentation may be Oxide-internal. Where possible, we're trying to move relevant documentation from those RFDs into docs here.
1616

17+
See also: link:../notes/multicast-architecture.adoc[Multicast Architecture: VLAN Scope]
18+
1719
== What is the control plane
1820

1921
In software systems the terms **data plane** and **control plane** are often used to refer to the parts of the system that directly provide resources to users (the data plane) and the parts that support the configuration, control, monitoring, and operation of the system (the control plane). Within the Oxide system, we say that the data plane comprises those parts that provide CPU resources (including both the host CPU and hypervisor software), storage resources, and network resources. The control plane provides the APIs through which users provision, configure, and monitor these resources and the mechanisms through which these APIs are implemented. Also part of the control plane are the APIs and facilities through which operators manage the system itself, including fault management, alerting, software updates for various components of the system, and so on.

docs/networking.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
This is a very rough introduction to how networking works within the Oxide system and particularly the control plane (Omicron). Much more information is available in various RFDs, particularly <<rfd63>>.
88

9+
See also: link:../notes/multicast-architecture.adoc[Multicast Architecture: VLAN Scope]
10+
911
== IPv6: the least you need to know
1012

1113
While IPv4 can be used for connectivity between Omicron and the outside world, everything else in the system uses IPv6. This section provides a _very_ cursory introduction to IPv6 for people only familiar with IPv4. You can skip this if you know IPv6. If you want slightly more detail than what's here, see https://www.roesen.org/files/ipv6_cheat_sheet.pdf[this cheat sheet].

end-to-end-tests/src/bin/bootstrap.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ async fn run_test() -> Result<()> {
5353
name: pool_name.parse().unwrap(),
5454
description: "Default IP pool".to_string(),
5555
ip_version,
56-
mvlan: None,
5756
pool_type: IpPoolType::Unicast,
58-
switch_port_uplinks: None,
5957
})
6058
.send()
6159
.await?;

end-to-end-tests/src/bin/commtest.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,7 @@ async fn rack_prepare(
295295
name: pool_name.parse().unwrap(),
296296
description: "Default IP pool".to_string(),
297297
ip_version,
298-
mvlan: None,
299298
pool_type: IpPoolType::Unicast,
300-
switch_port_uplinks: None,
301299
})
302300
.send()
303301
.await?;

nexus/db-model/src/ip_pool.rs

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
//! Model types for IP Pools and the CIDR blocks therein.
66
77
use crate::Name;
8-
use crate::SqlU16;
98
use crate::collection::DatastoreCollectionConfig;
109
use crate::impl_enum_type;
1110
use chrono::DateTime;
@@ -21,7 +20,6 @@ use nexus_types::external_api::shared;
2120
use nexus_types::external_api::views;
2221
use nexus_types::identity::Resource;
2322
use omicron_common::api::external;
24-
use omicron_common::vlan::VlanID;
2523
use std::net::IpAddr;
2624
use uuid::Uuid;
2725

@@ -105,12 +103,6 @@ pub struct IpPool {
105103
pub ip_version: IpVersion,
106104
/// Pool type for unicast (default) vs multicast pools.
107105
pub pool_type: IpPoolType,
108-
/// Switch port uplinks for multicast pools (array of switch port UUIDs).
109-
/// Only applies to multicast pools, None for unicast pools.
110-
pub switch_port_uplinks: Option<Vec<Uuid>>,
111-
/// MVLAN ID for multicast pools.
112-
/// Only applies to multicast pools, None for unicast pools.
113-
pub mvlan: Option<SqlU16>,
114106
/// Child resource generation number, for optimistic concurrency control of
115107
/// the contained ranges.
116108
pub rcgen: i64,
@@ -129,8 +121,6 @@ impl IpPool {
129121
),
130122
ip_version,
131123
pool_type: IpPoolType::Unicast,
132-
switch_port_uplinks: None,
133-
mvlan: None,
134124
rcgen: 0,
135125
}
136126
}
@@ -139,8 +129,6 @@ impl IpPool {
139129
pub fn new_multicast(
140130
pool_identity: &external::IdentityMetadataCreateParams,
141131
ip_version: IpVersion,
142-
switch_port_uplinks: Option<Vec<Uuid>>,
143-
mvlan: Option<VlanID>,
144132
) -> Self {
145133
Self {
146134
identity: IpPoolIdentity::new(
@@ -149,8 +137,6 @@ impl IpPool {
149137
),
150138
ip_version,
151139
pool_type: IpPoolType::Multicast,
152-
switch_port_uplinks,
153-
mvlan: mvlan.map(|vid| u16::from(vid).into()),
154140
rcgen: 0,
155141
}
156142
}
@@ -173,23 +159,10 @@ impl From<IpPool> for views::IpPool {
173159
let identity = pool.identity();
174160
let pool_type = pool.pool_type;
175161

176-
// Note: UUIDs expected to be converted to "switch.port" format in app
177-
// layer, upon retrieval.
178-
let switch_port_uplinks = match pool.switch_port_uplinks {
179-
Some(uuid_list) => Some(
180-
uuid_list.into_iter().map(|uuid| uuid.to_string()).collect(),
181-
),
182-
None => None,
183-
};
184-
185-
let mvlan = pool.mvlan.map(|vlan| vlan.into());
186-
187162
Self {
188163
identity,
189164
pool_type: pool_type.into(),
190165
ip_version: pool.ip_version.into(),
191-
switch_port_uplinks,
192-
mvlan,
193166
}
194167
}
195168
}
@@ -203,22 +176,14 @@ impl From<IpPool> for views::IpPool {
203176
pub struct IpPoolUpdate {
204177
pub name: Option<Name>,
205178
pub description: Option<String>,
206-
/// Switch port uplinks for multicast pools (array of switch port UUIDs),
207-
/// used for multicast traffic outbound from the rack to external networks.
208-
pub switch_port_uplinks: Option<Vec<Uuid>>,
209-
/// MVLAN ID for multicast pools.
210-
pub mvlan: Option<SqlU16>,
211179
pub time_modified: DateTime<Utc>,
212180
}
213181

214-
// Used for unicast updates.
215182
impl From<params::IpPoolUpdate> for IpPoolUpdate {
216183
fn from(params: params::IpPoolUpdate) -> Self {
217184
Self {
218185
name: params.identity.name.map(|n| n.into()),
219186
description: params.identity.description,
220-
switch_port_uplinks: None, // no change
221-
mvlan: None, // no change
222187
time_modified: Utc::now(),
223188
}
224189
}

nexus/db-queries/src/db/datastore/ip_pool.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,8 +1818,6 @@ mod test {
18181818
ip_version,
18191819
rcgen: 0,
18201820
pool_type: IpPoolType::Unicast,
1821-
mvlan: None,
1822-
switch_port_uplinks: None,
18231821
};
18241822
let pool = datastore
18251823
.ip_pool_create(&opctx, params)
@@ -2187,7 +2185,7 @@ mod test {
21872185
let pool = datastore
21882186
.ip_pool_create(
21892187
&opctx,
2190-
IpPool::new_multicast(&identity, IpVersion::V4, None, None),
2188+
IpPool::new_multicast(&identity, IpVersion::V4),
21912189
)
21922190
.await
21932191
.expect("Failed to create multicast IP pool");
@@ -2257,7 +2255,7 @@ mod test {
22572255
let pool = datastore
22582256
.ip_pool_create(
22592257
&opctx,
2260-
IpPool::new_multicast(&identity, IpVersion::V4, None, None),
2258+
IpPool::new_multicast(&identity, IpVersion::V4),
22612259
)
22622260
.await
22632261
.expect("Failed to create multicast IP pool");
@@ -2306,8 +2304,6 @@ mod test {
23062304
IpPool::new_multicast(
23072305
&ipv4_identity,
23082306
IpVersion::V4,
2309-
None,
2310-
None,
23112307
),
23122308
)
23132309
.await
@@ -2348,8 +2344,6 @@ mod test {
23482344
IpPool::new_multicast(
23492345
&ipv6_identity,
23502346
IpVersion::V6,
2351-
None,
2352-
None,
23532347
),
23542348
)
23552349
.await

nexus/db-schema/src/schema.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,6 @@ table! {
630630
time_deleted -> Nullable<Timestamptz>,
631631
ip_version -> crate::enums::IpVersionEnum,
632632
pool_type -> crate::enums::IpPoolTypeEnum,
633-
switch_port_uplinks -> Nullable<Array<Uuid>>,
634-
mvlan -> Nullable<Int4>,
635633
rcgen -> Int8,
636634
}
637635
}

nexus/src/app/ip_pool.rs

Lines changed: 5 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
77
use crate::external_api::params;
88
use crate::external_api::shared;
9-
use crate::external_api::views;
10-
use chrono::Utc;
119
use ipnetwork::IpNetwork;
1210
use nexus_db_lookup::LookupPath;
1311
use nexus_db_lookup::lookup;
@@ -80,38 +78,12 @@ impl super::Nexus {
8078
// https://github.com/oxidecomputer/omicron/issues/8881
8179
let ip_version = pool_params.ip_version.into();
8280

83-
let pool = match (
84-
pool_params.pool_type.clone(),
85-
pool_params.switch_port_uplinks.is_some(),
86-
) {
87-
(shared::IpPoolType::Unicast, true) => {
88-
return Err(Error::invalid_request(
89-
"switch_port_uplinks are only allowed for multicast IP pools",
90-
));
91-
}
92-
(shared::IpPoolType::Unicast, false) => {
93-
if pool_params.mvlan.is_some() {
94-
return Err(Error::invalid_request(
95-
"mvlan is only allowed for multicast IP pools",
96-
));
97-
}
81+
let pool = match pool_params.pool_type.clone() {
82+
shared::IpPoolType::Unicast => {
9883
IpPool::new(&pool_params.identity, ip_version)
9984
}
100-
(shared::IpPoolType::Multicast, _) => {
101-
let switch_port_ids = self
102-
.resolve_switch_port_ids(
103-
opctx,
104-
self.rack_id(),
105-
&pool_params.switch_port_uplinks,
106-
)
107-
.await?;
108-
109-
IpPool::new_multicast(
110-
&pool_params.identity,
111-
ip_version,
112-
switch_port_ids,
113-
pool_params.mvlan,
114-
)
85+
shared::IpPoolType::Multicast => {
86+
IpPool::new_multicast(&pool_params.identity, ip_version)
11587
}
11688
};
11789

@@ -316,21 +288,7 @@ impl super::Nexus {
316288
return Err(not_found_from_lookup(pool_lookup));
317289
}
318290

319-
let switch_port_ids = self
320-
.resolve_switch_port_ids(
321-
opctx,
322-
self.rack_id(),
323-
&updates.switch_port_uplinks,
324-
)
325-
.await?;
326-
327-
let updates_db = IpPoolUpdate {
328-
name: updates.identity.name.clone().map(Into::into),
329-
description: updates.identity.description.clone(),
330-
switch_port_uplinks: switch_port_ids,
331-
mvlan: updates.mvlan.map(|vid| u16::from(vid).into()),
332-
time_modified: Utc::now(),
333-
};
291+
let updates_db = IpPoolUpdate::from(updates.clone());
334292

335293
self.db_datastore.ip_pool_update(opctx, &authz_pool, updates_db).await
336294
}
@@ -544,99 +502,4 @@ impl super::Nexus {
544502
opctx.authorize(authz::Action::Modify, &authz_pool).await?;
545503
self.db_datastore.ip_pool_delete_range(opctx, &authz_pool, range).await
546504
}
547-
548-
async fn resolve_switch_port_ids(
549-
&self,
550-
opctx: &OpContext,
551-
rack_id: Uuid,
552-
uplinks: &Option<Vec<params::SwitchPortUplink>>,
553-
) -> Result<Option<Vec<Uuid>>, Error> {
554-
match uplinks {
555-
None => Ok(None),
556-
Some(list) => {
557-
let mut ids = Vec::with_capacity(list.len());
558-
559-
for uplink in list {
560-
let switch_location =
561-
Name::from(uplink.switch_location.clone());
562-
let port_name = Name::from(uplink.port_name.clone());
563-
let id = self
564-
.db_datastore
565-
.switch_port_get_id(
566-
opctx,
567-
rack_id,
568-
switch_location,
569-
port_name,
570-
)
571-
.await
572-
.map_err(|_| {
573-
Error::invalid_value(
574-
"switch_port_uplinks",
575-
format!("Switch port '{}' not found", uplink),
576-
)
577-
})?;
578-
ids.push(id);
579-
}
580-
Ok(Some(ids))
581-
}
582-
}
583-
}
584-
585-
/// Convert IP pool with proper switch port name resolution in an async
586-
/// context.
587-
pub(crate) async fn ip_pool_to_view(
588-
&self,
589-
opctx: &OpContext,
590-
pool: db::model::IpPool,
591-
) -> Result<views::IpPool, Error> {
592-
let identity = pool.identity();
593-
let pool_type = pool.pool_type;
594-
595-
// Convert switch port UUIDs to "switch.port" format
596-
let switch_port_uplinks = self
597-
.resolve_switch_port_names(opctx, &pool.switch_port_uplinks)
598-
.await?;
599-
600-
let mvlan = pool.mvlan.map(|vlan| vlan.into());
601-
602-
Ok(views::IpPool {
603-
identity,
604-
ip_version: pool.ip_version.into(),
605-
pool_type: pool_type.into(),
606-
switch_port_uplinks,
607-
mvlan,
608-
})
609-
}
610-
611-
// Convert switch port UUIDs to "switch.port" format for views
612-
async fn resolve_switch_port_names(
613-
&self,
614-
opctx: &OpContext,
615-
switch_port_ids: &Option<Vec<Uuid>>,
616-
) -> Result<Option<Vec<String>>, Error> {
617-
match switch_port_ids {
618-
None => Ok(None),
619-
Some(ids) => {
620-
let mut names = Vec::with_capacity(ids.len());
621-
for &id in ids {
622-
let switch_port = self
623-
.db_datastore
624-
.switch_port_get(opctx, id)
625-
.await
626-
.map_err(|_| {
627-
Error::internal_error(&format!(
628-
"Switch port with ID {} not found",
629-
id
630-
))
631-
})?;
632-
let name = format!(
633-
"{}.{}",
634-
switch_port.switch_location, switch_port.port_name
635-
);
636-
names.push(name);
637-
}
638-
Ok(Some(names))
639-
}
640-
}
641-
}
642505
}

nexus/src/external_api/http_entrypoints.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,8 +1775,7 @@ impl NexusExternalApi for NexusExternalApiImpl {
17751775
let opctx =
17761776
crate::context::op_context_for_external_api(&rqctx).await?;
17771777
let pool = nexus.ip_pool_create(&opctx, &pool_params).await?;
1778-
let pool_view = nexus.ip_pool_to_view(&opctx, pool).await?;
1779-
Ok(HttpResponseCreated(pool_view))
1778+
Ok(HttpResponseCreated(pool.into()))
17801779
};
17811780
apictx
17821781
.context

nexus/tests/integration_tests/endpoints.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -953,8 +953,6 @@ pub static DEMO_IP_POOL_UPDATE: LazyLock<params::IpPoolUpdate> =
953953
name: None,
954954
description: Some(String::from("a new IP pool")),
955955
},
956-
mvlan: None,
957-
switch_port_uplinks: None,
958956
});
959957
pub static DEMO_IP_POOL_SILOS_URL: LazyLock<String> =
960958
LazyLock::new(|| format!("{}/silos", *DEMO_IP_POOL_URL));

0 commit comments

Comments
 (0)