Skip to content

Commit 2850102

Browse files
authored
(2/5) [nexus] Add Affinity/Anti-Affinity groups to database (#7444)
Pulled out of #7076 Updates schema to include Affinity/Anti-Affinity groups, but does not use these schemas yet.
1 parent 5b432e4 commit 2850102

File tree

28 files changed

+582
-44
lines changed

28 files changed

+582
-44
lines changed

dev-tools/omdb/src/bin/omdb/db.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6453,6 +6453,7 @@ async fn cmd_db_vmm_info(
64536453
rss_ram: ByteCount(rss),
64546454
reservoir_ram: ByteCount(reservoir),
64556455
},
6456+
instance_id: _,
64566457
} = resource;
64576458
const SLED_ID: &'static str = "sled ID";
64586459
const THREADS: &'static str = "hardware threads";

nexus/db-model/src/affinity.rs

+259
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at https://mozilla.org/MPL/5.0/.
4+
5+
// Copyright 2025 Oxide Computer Company
6+
7+
//! Database representation of affinity and anti-affinity groups
8+
9+
use super::impl_enum_type;
10+
use super::Name;
11+
use crate::schema::affinity_group;
12+
use crate::schema::affinity_group_instance_membership;
13+
use crate::schema::anti_affinity_group;
14+
use crate::schema::anti_affinity_group_instance_membership;
15+
use crate::typed_uuid::DbTypedUuid;
16+
use chrono::{DateTime, Utc};
17+
use db_macros::Resource;
18+
use nexus_types::external_api::params;
19+
use nexus_types::external_api::views;
20+
use omicron_common::api::external;
21+
use omicron_common::api::external::IdentityMetadata;
22+
use omicron_uuid_kinds::AffinityGroupKind;
23+
use omicron_uuid_kinds::AffinityGroupUuid;
24+
use omicron_uuid_kinds::AntiAffinityGroupKind;
25+
use omicron_uuid_kinds::AntiAffinityGroupUuid;
26+
use omicron_uuid_kinds::InstanceKind;
27+
use omicron_uuid_kinds::InstanceUuid;
28+
use uuid::Uuid;
29+
30+
impl_enum_type!(
31+
#[derive(SqlType, Debug, QueryId)]
32+
#[diesel(postgres_type(name = "affinity_policy", schema = "public"))]
33+
pub struct AffinityPolicyEnum;
34+
35+
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow, PartialEq, Eq, Ord, PartialOrd)]
36+
#[diesel(sql_type = AffinityPolicyEnum)]
37+
pub enum AffinityPolicy;
38+
39+
// Enum values
40+
Fail => b"fail"
41+
Allow => b"allow"
42+
);
43+
44+
impl From<AffinityPolicy> for external::AffinityPolicy {
45+
fn from(policy: AffinityPolicy) -> Self {
46+
match policy {
47+
AffinityPolicy::Fail => Self::Fail,
48+
AffinityPolicy::Allow => Self::Allow,
49+
}
50+
}
51+
}
52+
53+
impl From<external::AffinityPolicy> for AffinityPolicy {
54+
fn from(policy: external::AffinityPolicy) -> Self {
55+
match policy {
56+
external::AffinityPolicy::Fail => Self::Fail,
57+
external::AffinityPolicy::Allow => Self::Allow,
58+
}
59+
}
60+
}
61+
62+
impl_enum_type!(
63+
#[derive(SqlType, Debug)]
64+
#[diesel(postgres_type(name = "failure_domain", schema = "public"))]
65+
pub struct FailureDomainEnum;
66+
67+
#[derive(Clone, Copy, Debug, AsExpression, FromSqlRow, PartialEq)]
68+
#[diesel(sql_type = FailureDomainEnum)]
69+
pub enum FailureDomain;
70+
71+
// Enum values
72+
Sled => b"sled"
73+
);
74+
75+
impl From<FailureDomain> for external::FailureDomain {
76+
fn from(domain: FailureDomain) -> Self {
77+
match domain {
78+
FailureDomain::Sled => Self::Sled,
79+
}
80+
}
81+
}
82+
83+
impl From<external::FailureDomain> for FailureDomain {
84+
fn from(domain: external::FailureDomain) -> Self {
85+
match domain {
86+
external::FailureDomain::Sled => Self::Sled,
87+
}
88+
}
89+
}
90+
91+
#[derive(
92+
Queryable, Insertable, Clone, Debug, Resource, Selectable, PartialEq,
93+
)]
94+
#[diesel(table_name = affinity_group)]
95+
pub struct AffinityGroup {
96+
#[diesel(embed)]
97+
pub identity: AffinityGroupIdentity,
98+
pub project_id: Uuid,
99+
pub policy: AffinityPolicy,
100+
pub failure_domain: FailureDomain,
101+
}
102+
103+
impl AffinityGroup {
104+
pub fn new(project_id: Uuid, params: params::AffinityGroupCreate) -> Self {
105+
Self {
106+
identity: AffinityGroupIdentity::new(
107+
Uuid::new_v4(),
108+
params.identity,
109+
),
110+
project_id,
111+
policy: params.policy.into(),
112+
failure_domain: params.failure_domain.into(),
113+
}
114+
}
115+
}
116+
117+
impl From<AffinityGroup> for views::AffinityGroup {
118+
fn from(group: AffinityGroup) -> Self {
119+
let identity = IdentityMetadata {
120+
id: group.identity.id,
121+
name: group.identity.name.into(),
122+
description: group.identity.description,
123+
time_created: group.identity.time_created,
124+
time_modified: group.identity.time_modified,
125+
};
126+
Self {
127+
identity,
128+
policy: group.policy.into(),
129+
failure_domain: group.failure_domain.into(),
130+
}
131+
}
132+
}
133+
134+
/// Describes a set of updates for the [`AffinityGroup`] model.
135+
#[derive(AsChangeset)]
136+
#[diesel(table_name = affinity_group)]
137+
pub struct AffinityGroupUpdate {
138+
pub name: Option<Name>,
139+
pub description: Option<String>,
140+
pub time_modified: DateTime<Utc>,
141+
}
142+
143+
impl From<params::AffinityGroupUpdate> for AffinityGroupUpdate {
144+
fn from(params: params::AffinityGroupUpdate) -> Self {
145+
Self {
146+
name: params.identity.name.map(Name),
147+
description: params.identity.description,
148+
time_modified: Utc::now(),
149+
}
150+
}
151+
}
152+
153+
#[derive(
154+
Queryable, Insertable, Clone, Debug, Resource, Selectable, PartialEq,
155+
)]
156+
#[diesel(table_name = anti_affinity_group)]
157+
pub struct AntiAffinityGroup {
158+
#[diesel(embed)]
159+
identity: AntiAffinityGroupIdentity,
160+
pub project_id: Uuid,
161+
pub policy: AffinityPolicy,
162+
pub failure_domain: FailureDomain,
163+
}
164+
165+
impl AntiAffinityGroup {
166+
pub fn new(
167+
project_id: Uuid,
168+
params: params::AntiAffinityGroupCreate,
169+
) -> Self {
170+
Self {
171+
identity: AntiAffinityGroupIdentity::new(
172+
Uuid::new_v4(),
173+
params.identity,
174+
),
175+
project_id,
176+
policy: params.policy.into(),
177+
failure_domain: params.failure_domain.into(),
178+
}
179+
}
180+
}
181+
182+
impl From<AntiAffinityGroup> for views::AntiAffinityGroup {
183+
fn from(group: AntiAffinityGroup) -> Self {
184+
let identity = IdentityMetadata {
185+
id: group.identity.id,
186+
name: group.identity.name.into(),
187+
description: group.identity.description,
188+
time_created: group.identity.time_created,
189+
time_modified: group.identity.time_modified,
190+
};
191+
Self {
192+
identity,
193+
policy: group.policy.into(),
194+
failure_domain: group.failure_domain.into(),
195+
}
196+
}
197+
}
198+
199+
/// Describes a set of updates for the [`AntiAffinityGroup`] model.
200+
#[derive(AsChangeset)]
201+
#[diesel(table_name = anti_affinity_group)]
202+
pub struct AntiAffinityGroupUpdate {
203+
pub name: Option<Name>,
204+
pub description: Option<String>,
205+
pub time_modified: DateTime<Utc>,
206+
}
207+
208+
impl From<params::AntiAffinityGroupUpdate> for AntiAffinityGroupUpdate {
209+
fn from(params: params::AntiAffinityGroupUpdate) -> Self {
210+
Self {
211+
name: params.identity.name.map(Name),
212+
description: params.identity.description,
213+
time_modified: Utc::now(),
214+
}
215+
}
216+
}
217+
218+
#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
219+
#[diesel(table_name = affinity_group_instance_membership)]
220+
pub struct AffinityGroupInstanceMembership {
221+
pub group_id: DbTypedUuid<AffinityGroupKind>,
222+
pub instance_id: DbTypedUuid<InstanceKind>,
223+
}
224+
225+
impl AffinityGroupInstanceMembership {
226+
pub fn new(group_id: AffinityGroupUuid, instance_id: InstanceUuid) -> Self {
227+
Self { group_id: group_id.into(), instance_id: instance_id.into() }
228+
}
229+
}
230+
231+
impl From<AffinityGroupInstanceMembership> for external::AffinityGroupMember {
232+
fn from(member: AffinityGroupInstanceMembership) -> Self {
233+
Self::Instance(member.instance_id.into())
234+
}
235+
}
236+
237+
#[derive(Queryable, Insertable, Clone, Debug, Selectable)]
238+
#[diesel(table_name = anti_affinity_group_instance_membership)]
239+
pub struct AntiAffinityGroupInstanceMembership {
240+
pub group_id: DbTypedUuid<AntiAffinityGroupKind>,
241+
pub instance_id: DbTypedUuid<InstanceKind>,
242+
}
243+
244+
impl AntiAffinityGroupInstanceMembership {
245+
pub fn new(
246+
group_id: AntiAffinityGroupUuid,
247+
instance_id: InstanceUuid,
248+
) -> Self {
249+
Self { group_id: group_id.into(), instance_id: instance_id.into() }
250+
}
251+
}
252+
253+
impl From<AntiAffinityGroupInstanceMembership>
254+
for external::AntiAffinityGroupMember
255+
{
256+
fn from(member: AntiAffinityGroupInstanceMembership) -> Self {
257+
Self::Instance(member.instance_id.into())
258+
}
259+
}

nexus/db-model/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern crate diesel;
1010
extern crate newtype_derive;
1111

1212
mod address_lot;
13+
mod affinity;
1314
mod allow_list;
1415
mod bfd;
1516
mod bgp;
@@ -130,6 +131,7 @@ mod db {
130131
pub use self::macaddr::*;
131132
pub use self::unsigned::*;
132133
pub use address_lot::*;
134+
pub use affinity::*;
133135
pub use allow_list::*;
134136
pub use bfd::*;
135137
pub use bgp::*;

nexus/db-model/src/project.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5-
use super::{Disk, Generation, Instance, Name, Snapshot, Vpc};
5+
use super::{
6+
AffinityGroup, AntiAffinityGroup, Disk, Generation, Instance, Name,
7+
Snapshot, Vpc,
8+
};
69
use crate::collection::DatastoreCollectionConfig;
7-
use crate::schema::{disk, image, instance, project, snapshot, vpc};
10+
use crate::schema::{
11+
affinity_group, anti_affinity_group, disk, image, instance, project,
12+
snapshot, vpc,
13+
};
814
use crate::Image;
915
use chrono::{DateTime, Utc};
1016
use db_macros::Resource;
@@ -69,6 +75,20 @@ impl DatastoreCollectionConfig<Instance> for Project {
6975
type CollectionIdColumn = instance::dsl::project_id;
7076
}
7177

78+
impl DatastoreCollectionConfig<AffinityGroup> for Project {
79+
type CollectionId = Uuid;
80+
type GenerationNumberColumn = project::dsl::rcgen;
81+
type CollectionTimeDeletedColumn = project::dsl::time_deleted;
82+
type CollectionIdColumn = affinity_group::dsl::project_id;
83+
}
84+
85+
impl DatastoreCollectionConfig<AntiAffinityGroup> for Project {
86+
type CollectionId = Uuid;
87+
type GenerationNumberColumn = project::dsl::rcgen;
88+
type CollectionTimeDeletedColumn = project::dsl::time_deleted;
89+
type CollectionIdColumn = anti_affinity_group::dsl::project_id;
90+
}
91+
7292
impl DatastoreCollectionConfig<Disk> for Project {
7393
type CollectionId = Uuid;
7494
type GenerationNumberColumn = project::dsl::rcgen;

nexus/db-model/src/schema.rs

+48-1
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,48 @@ table! {
468468
}
469469
}
470470

471+
table! {
472+
affinity_group (id) {
473+
id -> Uuid,
474+
name -> Text,
475+
description -> Text,
476+
time_created -> Timestamptz,
477+
time_modified -> Timestamptz,
478+
time_deleted -> Nullable<Timestamptz>,
479+
project_id -> Uuid,
480+
policy -> crate::AffinityPolicyEnum,
481+
failure_domain -> crate::FailureDomainEnum,
482+
}
483+
}
484+
485+
table! {
486+
anti_affinity_group (id) {
487+
id -> Uuid,
488+
name -> Text,
489+
description -> Text,
490+
time_created -> Timestamptz,
491+
time_modified -> Timestamptz,
492+
time_deleted -> Nullable<Timestamptz>,
493+
project_id -> Uuid,
494+
policy -> crate::AffinityPolicyEnum,
495+
failure_domain -> crate::FailureDomainEnum,
496+
}
497+
}
498+
499+
table! {
500+
affinity_group_instance_membership (group_id, instance_id) {
501+
group_id -> Uuid,
502+
instance_id -> Uuid,
503+
}
504+
}
505+
506+
table! {
507+
anti_affinity_group_instance_membership (group_id, instance_id) {
508+
group_id -> Uuid,
509+
instance_id -> Uuid,
510+
}
511+
}
512+
471513
table! {
472514
metric_producer (id) {
473515
id -> Uuid,
@@ -915,10 +957,11 @@ table! {
915957
sled_resource (id) {
916958
id -> Uuid,
917959
sled_id -> Uuid,
918-
kind -> crate::SledResourceKindEnum,
919960
hardware_threads -> Int8,
920961
rss_ram -> Int8,
921962
reservoir_ram -> Int8,
963+
kind -> crate::SledResourceKindEnum,
964+
instance_id -> Nullable<Uuid>,
922965
}
923966
}
924967

@@ -2029,6 +2072,10 @@ allow_tables_to_appear_in_same_query!(
20292072
allow_tables_to_appear_in_same_query!(hw_baseboard_id, inv_sled_agent,);
20302073

20312074
allow_tables_to_appear_in_same_query!(
2075+
anti_affinity_group,
2076+
anti_affinity_group_instance_membership,
2077+
affinity_group,
2078+
affinity_group_instance_membership,
20322079
bp_omicron_zone,
20332080
bp_target,
20342081
rendezvous_debug_dataset,

0 commit comments

Comments
 (0)