Skip to content

Commit

Permalink
The sagaization begins
Browse files Browse the repository at this point in the history
  • Loading branch information
FelixMcFelix committed Dec 13, 2023
1 parent eddd29c commit 34c03d7
Show file tree
Hide file tree
Showing 9 changed files with 662 additions and 93 deletions.
6 changes: 2 additions & 4 deletions nexus/db-queries/src/db/datastore/external_ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ impl DataStore {
authz_fip: &authz::FloatingIp,
db_fip: &FloatingIp,
instance_id: Uuid,
) -> UpdateResult<(FloatingIp, Option<Uuid>)> {
) -> UpdateResult<FloatingIp> {
use db::schema::external_ip::dsl;

// Verify this FIP is not attached to any instances/services.
Expand All @@ -485,8 +485,6 @@ impl DataStore {
opctx.authorize(authz::Action::Modify, authz_fip).await?;
opctx.authorize(authz::Action::Modify, &authz_instance).await?;

let i = self.instance_fetch_with_vmm(opctx, &authz_instance).await?;

let out = diesel::update(dsl::external_ip)
.filter(dsl::id.eq(db_fip.id()))
.filter(dsl::kind.eq(IpKind::Floating))
Expand All @@ -508,7 +506,7 @@ impl DataStore {
.and_then(|r| FloatingIp::try_from(r))
.map_err(|e| Error::internal_error(&format!("{e}")))?;

Ok((out, i.sled_id()))
Ok(out)
}

/// Detaches a Floating IP address from an instance.
Expand Down
101 changes: 42 additions & 59 deletions nexus/src/app/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1874,68 +1874,37 @@ impl super::Nexus {
Ok(())
}

/// Detach a disk from an instance.
/// Attach a disk to an instance.
pub(crate) async fn instance_attach_external_ip(
&self,
self: Arc<Self>,
opctx: &OpContext,
instance_lookup: &lookup::Instance<'_>,
ext_ip: &params::ExternalIpCreate,
) -> UpdateResult<views::ExternalIp> {
let (.., authz_project, authz_instance) =
instance_lookup.lookup_for(authz::Action::Modify).await?;
let (.., authz_project, authz_instance, instance) =
instance_lookup.fetch_for(authz::Action::Modify).await?;

let (authz_fip, db_fip) = match ext_ip {
params::ExternalIpCreate::Ephemeral { pool_name } => Err(Error::internal_error("ephemeral IP attach/detach not yet supported"))?,
params::ExternalIpCreate::Floating { floating_ip_name } => {
let floating_ip_name = db::model::Name(floating_ip_name.clone());
let (.., authz_fip, db_fip) = LookupPath::new(&opctx, &self.datastore())
.project_id(authz_project.id())
.floating_ip_name(&floating_ip_name)
.fetch_for(authz::Action::Modify)
.await?;
(authz_fip, db_fip)
},
let saga_params = sagas::instance_ip_attach::Params {
create_params: ext_ip.clone(),
authz_instance,
instance,
ephemeral_ip_id: Uuid::new_v4(),
serialized_authn: authn::saga::Serialized::for_opctx(opctx),
};

let (eip, sled_uuid) = self
.datastore()
.floating_ip_attach(opctx, &authz_fip, &db_fip, authz_instance.id())
let saga_results = self
.execute_saga::<sagas::instance_ip_attach::SagaInstanceIpAttach>(
saga_params,
)
.await?;

if let Some(uuid) = sled_uuid {
self.sled_client(&uuid)
.await?
.instance_put_external_ip(&authz_instance.id(), &sled_agent_client::types::InstanceExternalIpBody::Floating(db_fip.ip.ip()))
.await?;

let (.., sled) = self.sled_lookup(opctx, &uuid)?.fetch().await?;

let boundary_switches = self.boundary_switches(opctx)
.await?;

for switch in boundary_switches {
let dpd_client =
self.dpd_clients.get(&switch).ok_or_else(|| {
Error::internal_error(&format!(
"unable to find client for switch {switch}"
))
})?;

self.instance_ensure_dpd_config(
&opctx,
authz_instance.id(),
&sled.address(),
None,
dpd_client,
)
.await?;
}
}
todo!()

Ok(views::ExternalIp::from(views::FloatingIp::from(eip)))
// XXX: add a From<db::External> for views::External
// Ok(views::ExternalIp::from(views::FloatingIp::from(eip)))
}

/// Detach a disk from an instance.
/// Detach an external IP from an instance.
pub(crate) async fn instance_detach_external_ip(
&self,
opctx: &OpContext,
Expand All @@ -1946,27 +1915,41 @@ impl super::Nexus {
instance_lookup.lookup_for(authz::Action::Modify).await?;

let (authz_fip, db_fip) = match ext_ip {
params::ExternalIpDelete::Ephemeral => Err(Error::internal_error("ephemeral IP attach/detach not yet supported"))?,
params::ExternalIpDelete::Ephemeral => Err(Error::internal_error(
"ephemeral IP attach/detach not yet supported",
))?,
params::ExternalIpDelete::Floating { floating_ip_name } => {
let floating_ip_name = db::model::Name(floating_ip_name.clone());
let (.., authz_fip, db_fip) = LookupPath::new(&opctx, &self.datastore())
.project_id(authz_project.id())
.floating_ip_name(&floating_ip_name)
.fetch_for(authz::Action::Modify)
.await?;
let floating_ip_name =
db::model::Name(floating_ip_name.clone());
let (.., authz_fip, db_fip) =
LookupPath::new(&opctx, &self.datastore())
.project_id(authz_project.id())
.floating_ip_name(&floating_ip_name)
.fetch_for(authz::Action::Modify)
.await?;
(authz_fip, db_fip)
},
}
};

let (eip, sled_uuid) = self
.datastore()
.floating_ip_detach(opctx, &authz_fip, &db_fip, Some(authz_instance.id()))
.floating_ip_detach(
opctx,
&authz_fip,
&db_fip,
Some(authz_instance.id()),
)
.await?;

if let Some(uuid) = sled_uuid {
self.sled_client(&uuid)
.await?
.instance_delete_external_ip(&authz_instance.id(), &sled_agent_client::types::InstanceExternalIpBody::Floating(db_fip.ip.ip()))
.instance_delete_external_ip(
&authz_instance.id(),
&sled_agent_client::types::InstanceExternalIpBody::Floating(
db_fip.ip.ip(),
),
)
.await?;
}

Expand Down
30 changes: 11 additions & 19 deletions nexus/src/app/instance_network.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ impl super::Nexus {
opctx: &OpContext,
instance_id: Uuid,
sled_ip_address: &std::net::SocketAddrV6,
ip_index_filter: Option<usize>,
ip_filter: Option<Uuid>,
dpd_client: &Arc<dpd_client::Client>,
) -> Result<(), Error> {
let log = &self.log;
Expand Down Expand Up @@ -344,33 +344,25 @@ impl super::Nexus {
.instance_lookup_external_ips(&opctx, instance_id)
.await?;

if let Some(wanted_index) = ip_index_filter {
if let None = ips.get(wanted_index) {
let ips_of_interest = if let Some(wanted_id) = ip_filter {
if let Some(ip) = ips.iter().find(|v| v.id == wanted_id) {
std::slice::from_ref(ip)
} else {
return Err(Error::internal_error(&format!(
"failed to find external ip address at index: {}",
wanted_index
"failed to find external ip address with id: {wanted_id}",
)));
}
}
} else {
&ips[..]
};

let sled_address =
Ipv6Net(Ipv6Network::new(*sled_ip_address.ip(), 128).unwrap());

for target_ip in ips
.iter()
.enumerate()
.filter(|(index, _)| {
if let Some(wanted_index) = ip_index_filter {
*index == wanted_index
} else {
true
}
})
.map(|(_, ip)| ip)
{
for external_ip in ips_of_interest {
// For each external ip, add a nat entry to the database
self.ensure_nat_entry(
target_ip,
external_ip,
sled_address,
&network_interface,
mac_address,
Expand Down
9 changes: 8 additions & 1 deletion nexus/src/app/sagas/instance_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,14 @@ async fn sic_allocate_instance_external_ip_undo(
.fetch_for(authz::Action::Modify)
.await?;

datastore.floating_ip_detach(&opctx, &authz_fip, &db_fip).await?;
datastore
.floating_ip_detach(
&opctx,
&authz_fip,
&db_fip,
Some(repeat_saga_params.instance_id),
)
.await?;
}
}
Ok(())
Expand Down
Loading

0 comments on commit 34c03d7

Please sign in to comment.