Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
1c9d172
[multicast] implicit group lifecycle with IP pool integration
zeeshanlakhani Nov 29, 2025
f7b87b4
[follow-up] link gateway should be unicast default pool only
zeeshanlakhani Dec 1, 2025
68a0df3
[multicast] Drop (m)vlan from multicast groups
zeeshanlakhani Nov 30, 2025
1e3b137
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 2, 2025
5717f67
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 2, 2025
4b62ccb
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 3, 2025
016f7e1
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 3, 2025
655e602
[update] auto-select appropriate SSM/ASM pool when allocating multica…
zeeshanlakhani Dec 3, 2025
e2a1281
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 3, 2025
7def773
[fix] auth ordering
zeeshanlakhani Dec 5, 2025
f28aa9c
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 5, 2025
bad9f11
[api] version multicast endpoints for implicit group lifecycle
zeeshanlakhani Dec 5, 2025
82fb9df
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 5, 2025
dac4df1
[api] prep for drop of vlan
zeeshanlakhani Dec 5, 2025
7b673a5
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 6, 2025
56d9a8b
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 6, 2025
c8242bc
[merge] external api clarity and udpates
zeeshanlakhani Dec 6, 2025
064268f
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 6, 2025
bc472b3
[fmt] ..
zeeshanlakhani Dec 6, 2025
5611baf
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 6, 2025
b12e8c1
[fix] minor updates and delegation for API
zeeshanlakhani Dec 6, 2025
b5ae188
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 6, 2025
029f9d6
[fmt] ..
zeeshanlakhani Dec 6, 2025
709c568
[multicast] Relax multicast address restrictions for flexibility
zeeshanlakhani Dec 9, 2025
39a016a
[dep] oxnet update
zeeshanlakhani Dec 9, 2025
07a36a1
[nit] expose vni
zeeshanlakhani Dec 9, 2025
952a4c8
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 9, 2025
a7bdb6c
..
zeeshanlakhani Dec 9, 2025
d940ab3
[review] source IPs per member + xor with salt underlay mapping
zeeshanlakhani Dec 16, 2025
3d4230c
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 16, 2025
dc791eb
[hakari] ..
zeeshanlakhani Dec 16, 2025
57fc06e
[fix] endpoint
zeeshanlakhani Dec 16, 2025
894d295
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 16, 2025
27bb9e0
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 16, 2025
6322c39
[post-merge] api fixins
zeeshanlakhani Dec 16, 2025
0e0737e
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 17, 2025
52a3d65
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Dec 17, 2025
9794876
[multicast] put_upsert test helper, pool selection tests, and ASM sou…
zeeshanlakhani Dec 17, 2025
bc20284
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 17, 2025
5ca441d
[post-merge] leftover cleanup during conflicts
zeeshanlakhani Dec 17, 2025
68a141c
[minor] missing wait
zeeshanlakhani Dec 17, 2025
7426d09
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Dec 17, 2025
f7f3c39
[minor] test fixup
zeeshanlakhani Dec 17, 2025
c11aae0
[ip-pools] Allow multiple default IP pools per silo
zeeshanlakhani Dec 23, 2025
c0e3fba
Merge remote-tracking branch 'origin/zl/multiple-default-pools' into …
zeeshanlakhani Dec 28, 2025
f7ef672
[multicast] Fix SSM source filtering to be more flexible/ASM
zeeshanlakhani Dec 28, 2025
93460f3
Merge remote-tracking branch 'origin/main' into zl/multiple-default-p…
zeeshanlakhani Dec 30, 2025
0fa862d
[review] check ip_version against pool + sql migration split
zeeshanlakhani Dec 30, 2025
24b435c
[review] doc comments
zeeshanlakhani Dec 30, 2025
cc4bca7
[openapi] regen docs
zeeshanlakhani Dec 31, 2025
9865c3f
Merge remote-tracking branch 'origin/zl/multiple-default-pools' into …
zeeshanlakhani Dec 31, 2025
bdfe893
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Jan 4, 2026
21e6b5d
[multicast] Add ip_version to join API for pool disambiguation+cleanup
zeeshanlakhani Jan 4, 2026
cf28577
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Jan 4, 2026
27e7f53
[multicast] Implicit lifecycle implementation and cleanup
zeeshanlakhani Jan 5, 2026
af93ec2
[doc-fix]
zeeshanlakhani Jan 5, 2026
76a9555
[db] constraint change migration
zeeshanlakhani Jan 6, 2026
b257fc0
[dupe] rm dupe code from merge
zeeshanlakhani Jan 6, 2026
b0cccf8
[minor] paging for instance groups
zeeshanlakhani Jan 7, 2026
ac1bcae
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Jan 9, 2026
0b26c5a
Merge remote-tracking branch 'origin/main' into zl/mcast-implicit-lif…
zeeshanlakhani Jan 9, 2026
bc47c1e
[post-merge] fix
zeeshanlakhani Jan 9, 2026
cbd97e4
[post-merge] fix
zeeshanlakhani Jan 9, 2026
04accd4
Merge remote-tracking branch 'origin/zl/mcast-implicit-lifecycle' int…
zeeshanlakhani Jan 11, 2026
1778d02
Merge remote-tracking branch 'origin/main' into zl/drop-mvlan-from-group
zeeshanlakhani Jan 11, 2026
b9b986a
[openapi] updates
zeeshanlakhani Jan 11, 2026
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
33 changes: 3 additions & 30 deletions nexus/db-model/src/multicast_group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,28 +178,6 @@ pub struct ExternalMulticastGroup {
pub ip_pool_range_id: Uuid,
/// Primary multicast IP address (overlay/external).
pub multicast_ip: IpNetwork,
/// Multicast VLAN (MVLAN) for egress multicast traffic to upstream networks.
///
/// When specified, this VLAN ID is passed to switches (via DPD) as part of
/// the `ExternalForwarding` configuration to tag multicast packets leaving
/// the rack. This enables multicast traffic to traverse VLAN-segmented
/// upstream networks (e.g., peering with external multicast sources/receivers
/// on specific VLANs).
///
/// The MVLAN value is sent to switches during group creation/updates and
/// controls VLAN tagging for egress traffic only; it does not affect ingress
/// multicast traffic received by the rack. Switch port selection for egress
/// traffic remains pending (see TODOs in `nexus/src/app/multicast/dataplane.rs`).
///
/// Valid range when specified: 2-4094 (IEEE 802.1Q; Dendrite requires >= 2).
///
/// Database Type: i16 (INT2) - this field uses `i16` (INT2) for storage
/// efficiency, unlike other VLAN columns in the schema which use `SqlU16`
/// (forcing INT4). Direct `i16` is appropriate here since VLANs fit in
/// INT2's range.
///
/// TODO(multicast): Remove mvlan field - being deprecated from multicast groups
pub mvlan: Option<i16>,
/// Associated underlay group for NAT.
/// Initially None in ["Creating"](MulticastGroupState::Creating) state,
/// populated by reconciler when group becomes ["Active"](MulticastGroupState::Active).
Expand Down Expand Up @@ -248,11 +226,11 @@ pub struct MulticastGroupMemberValues {
pub parent_id: Uuid,
pub sled_id: Option<DbTypedUuid<SledKind>>,
pub state: MulticastGroupMemberState,
// version_added and version_removed are omitted - database assigns these
// via DEFAULT nextval()
pub multicast_ip: IpNetwork,
/// Source IPs for source-filtered multicast (optional for ASM, required for SSM).
pub source_ips: Vec<IpNetwork>,
// version_added and version_removed are omitted - database assigns these
// via DEFAULT nextval()
}

/// A member of a multicast group (instance that receives multicast traffic).
Expand Down Expand Up @@ -329,9 +307,7 @@ impl TryFrom<MulticastGroupMember> for views::MulticastGroupMember {

/// An incomplete external multicast group, used to store state required for
/// issuing the database query that selects an available multicast IP and stores
/// the resulting record.
///
/// Note: tag is computed in SQL as `{uuid}:{multicast_ip}`.
/// the resulting record. Tag is computed in SQL as `{uuid}:{multicast_ip}`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IncompleteExternalMulticastGroup {
pub id: Uuid,
Expand All @@ -341,7 +317,6 @@ pub struct IncompleteExternalMulticastGroup {
pub ip_pool_id: Uuid,
/// Optional address requesting a specific multicast IP be allocated.
pub explicit_address: Option<IpNetwork>,
pub mvlan: Option<i16>,
pub vni: Vni,
}

Expand All @@ -353,7 +328,6 @@ pub struct IncompleteExternalMulticastGroupParams {
pub description: String,
pub ip_pool_id: Uuid,
pub explicit_address: Option<IpAddr>,
pub mvlan: Option<i16>,
pub vni: Vni,
}

Expand All @@ -367,7 +341,6 @@ impl IncompleteExternalMulticastGroup {
time_created: Utc::now(),
ip_pool_id: params.ip_pool_id,
explicit_address: params.explicit_address.map(|ip| ip.into()),
mvlan: params.mvlan,
vni: params.vni,
}
}
Expand Down
3 changes: 2 additions & 1 deletion nexus/db-model/src/schema_versions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use std::{collections::BTreeMap, sync::LazyLock};
///
/// This must be updated when you change the database schema. Refer to
/// schema/crdb/README.adoc in the root of this repository for details.
pub const SCHEMA_VERSION: Version = Version::new(220, 0, 0);
pub const SCHEMA_VERSION: Version = Version::new(221, 0, 0);

/// List of all past database schema versions, in *reverse* order
///
Expand All @@ -28,6 +28,7 @@ static KNOWN_VERSIONS: LazyLock<Vec<KnownVersion>> = LazyLock::new(|| {
// | leaving the first copy as an example for the next person.
// v
// KnownVersion::new(next_int, "unique-dirname-with-the-sql-files"),
KnownVersion::new(221, "multicast-drop-mvlan"),
KnownVersion::new(220, "multicast-implicit-lifecycle"),
KnownVersion::new(219, "blueprint-sled-last-used-ip"),
KnownVersion::new(218, "measurements"),
Expand Down
59 changes: 5 additions & 54 deletions nexus/db-queries/src/db/datastore/multicast/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ use omicron_common::api::external::{
IdentityMetadataCreateParams, IpVersion, ListResultVec, LookupResult,
LookupType, ResourceType, UpdateResult,
};
use omicron_common::vlan::VlanID;
use omicron_uuid_kinds::{GenericUuid, MulticastGroupUuid};

use super::EnsureUnderlayResult;
Expand All @@ -57,41 +56,22 @@ use crate::db::update_and_check::{UpdateAndCheck, UpdateStatus};
/// External multicast group with computed source IPs from members.
///
/// The `source_ips` field contains the union of all member source IPs,
/// computed via a separate query. This struct enables a clean `TryFrom`
/// conversion to the API view.
///
// TODO(multicast): Remove mvlan field, being deprecated from multicast groups
/// computed via a separate query.
#[derive(Clone, Debug)]
pub struct ExternalMulticastGroupWithSources {
pub group: ExternalMulticastGroup,
pub source_ips: Vec<IpAddr>,
}

impl TryFrom<ExternalMulticastGroupWithSources> for views::MulticastGroup {
type Error = external::Error;

fn try_from(
value: ExternalMulticastGroupWithSources,
) -> Result<Self, Self::Error> {
let mvlan = value
.group
.mvlan
.map(|vlan| VlanID::new(vlan as u16))
.transpose()
.map_err(|e| {
external::Error::internal_error(&format!(
"invalid VLAN ID: {e:#}"
))
})?;

Ok(views::MulticastGroup {
impl From<ExternalMulticastGroupWithSources> for views::MulticastGroup {
fn from(value: ExternalMulticastGroupWithSources) -> Self {
views::MulticastGroup {
identity: value.group.identity(),
multicast_ip: value.group.multicast_ip.ip(),
source_ips: value.source_ips,
mvlan,
ip_pool_id: value.group.ip_pool_id,
state: value.group.state.to_string(),
})
}
}
}

Expand All @@ -104,7 +84,6 @@ pub(crate) struct MulticastGroupAllocationParams {
pub identity: IdentityMetadataCreateParams,
pub ip: Option<IpAddr>,
pub pool: Option<authz::IpPool>,
pub mvlan: Option<VlanID>,
/// Derived for whether the joining member has source IPs.
/// Used for default pool selection -> if true, prefer SSM pool first.
pub has_sources: bool,
Expand Down Expand Up @@ -257,7 +236,6 @@ impl DataStore {
identity: params.identity.clone(),
ip: params.multicast_ip,
pool: authz_pool,
mvlan: params.mvlan,
has_sources: params.has_sources,
ip_version: params.ip_version,
},
Expand Down Expand Up @@ -608,7 +586,6 @@ impl DataStore {
description: params.identity.description.clone(),
ip_pool_id: authz_pool.id(),
explicit_address: params.ip,
mvlan: params.mvlan.map(|vlan_id| u16::from(vlan_id) as i16),
vni,
},
);
Expand Down Expand Up @@ -986,7 +963,6 @@ mod tests {
description: "First group".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -1002,7 +978,6 @@ mod tests {
description: "Second group".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -1018,7 +993,6 @@ mod tests {
description: "Should fail".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1092,7 +1066,6 @@ mod tests {
description: "Group using default pool".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -1118,7 +1091,6 @@ mod tests {
description: "Group with explicit pool".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1246,7 +1218,6 @@ mod tests {
description: "Comprehensive test group".to_string(),
},
multicast_ip: Some("224.1.3.3".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1348,7 +1319,6 @@ mod tests {
.to_string(),
},
multicast_ip: None, // Let it allocate from pool
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1562,7 +1532,6 @@ mod tests {
description: "Group for IP reuse test".to_string(),
},
multicast_ip: Some(target_ip),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1590,7 +1559,6 @@ mod tests {
description: "Second group reusing same IP".to_string(),
},
multicast_ip: Some(target_ip),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1676,7 +1644,6 @@ mod tests {
description: "First group to exhaust pool".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -1694,7 +1661,6 @@ mod tests {
description: "Second group should fail".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1725,7 +1691,6 @@ mod tests {
.to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1812,7 +1777,6 @@ mod tests {
description: "Group for deallocation testing".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -1937,7 +1901,6 @@ mod tests {
description: "Test group for fetch operations".to_string(),
},
multicast_ip: Some("224.100.10.5".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -2044,7 +2007,6 @@ mod tests {
description: "Fleet-wide group 1".to_string(),
},
multicast_ip: Some("224.100.20.10".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -2055,7 +2017,6 @@ mod tests {
description: "Fleet-wide group 2".to_string(),
},
multicast_ip: Some("224.100.20.11".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -2066,7 +2027,6 @@ mod tests {
description: "Fleet-wide group 3".to_string(),
},
multicast_ip: Some("224.100.20.12".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -2175,7 +2135,6 @@ mod tests {
description: "Test group for state transitions".to_string(),
},
multicast_ip: Some("224.100.30.5".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -2515,7 +2474,6 @@ mod tests {
description: "Group using ASM pool".to_string(),
},
multicast_ip: None, // No explicit IP -> triggers pool auto-selection
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -2606,7 +2564,6 @@ mod tests {
description: "Should fall back to ASM when no SSM".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: true,
ip_version: None,
};
Expand Down Expand Up @@ -2679,7 +2636,6 @@ mod tests {
description: "Should prefer SSM over ASM".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: true,
ip_version: None,
};
Expand All @@ -2703,7 +2659,6 @@ mod tests {
description: "has_sources=false should use ASM".to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -2838,7 +2793,6 @@ mod tests {
.to_string(),
},
multicast_ip: None,
mvlan: None,
has_sources: true,
ip_version: None,
};
Expand Down Expand Up @@ -2919,7 +2873,6 @@ mod tests {
description: "First group for collision test".to_string(),
},
multicast_ip: Some("224.10.1.1".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand All @@ -2935,7 +2888,6 @@ mod tests {
description: "Second group for collision test".to_string(),
},
multicast_ip: Some("224.10.1.2".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down Expand Up @@ -3059,7 +3011,6 @@ mod tests {
description: "Group for salt testing".to_string(),
},
multicast_ip: Some("224.20.1.1".parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down
1 change: 0 additions & 1 deletion nexus/db-queries/src/db/datastore/multicast/members.rs
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,6 @@ mod tests {
},
multicast_ip: Some("224.10.1.6".parse().unwrap()),
// Pool resolved via authz_pool argument to datastore call
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down
1 change: 0 additions & 1 deletion nexus/db-queries/src/db/pub_test_utils/multicast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@ pub async fn create_test_group_with_state(
description: format!("Test group: {group_name}"),
},
multicast_ip: Some(multicast_ip.parse().unwrap()),
mvlan: None,
has_sources: false,
ip_version: None,
};
Expand Down
Loading
Loading