Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions bin/propolis-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ impl DiskRequest {
backend_id: backend_id.clone(),
pci_path,
serial_number: nvme_serial_from_str(&self.name, b' '),
has_write_cache: false,
}),
_ => anyhow::bail!(
"invalid device type in disk request: {:?}",
Expand Down
1 change: 1 addition & 0 deletions bin/propolis-server/src/lib/initializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,7 @@ impl MachineInitializer<'_> {
let nvme = nvme::PciNvme::create(
&nvme_spec.serial_number,
mdts,
false,
self.log.new(slog::o!("component" => component)),
);
self.devices.insert(device_id.clone(), nvme.clone());
Expand Down
5 changes: 5 additions & 0 deletions bin/propolis-server/src/lib/migrate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! This module and its children define the migration protocol for
//! `propolis-server`. Generally the structures and state machine here are
//! consistent with the discussion in RFD 71.

use bit_field::BitField;
use dropshot::HttpError;
use propolis::migrate::MigrateStateError;
Expand All @@ -17,6 +21,7 @@ mod memx;
mod preamble;
pub mod protocol;
pub mod source;
mod types;

/// Trait bounds for connection objects used in live migrations.
pub(crate) trait MigrateConn:
Expand Down
76 changes: 5 additions & 71 deletions bin/propolis-server/src/lib/migrate/preamble.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@ use propolis_api_types::instance::ReplacementComponent;
use propolis_api_types_versions::v1;
use serde::{Deserialize, Serialize};

use crate::spec::{api_spec_v0::ApiSpecError, Spec};
use crate::migrate;
use crate::spec::Spec;

use super::MigrateError;

#[derive(Deserialize, Serialize, Debug)]
pub(crate) struct Preamble {
pub instance_spec: v1::instance_spec::VersionedInstanceSpec,
pub instance_spec: migrate::types::VersionedInstanceSpec,
pub blobs: Vec<Vec<u8>>,
}

impl Preamble {
pub fn new(
instance_spec: v1::instance_spec::VersionedInstanceSpec,
instance_spec: migrate::types::VersionedInstanceSpec,
) -> Preamble {
Preamble { instance_spec, blobs: Vec::new() }
}
Expand All @@ -40,75 +41,8 @@ impl Preamble {
ReplacementComponent,
>,
) -> Result<Spec, MigrateError> {
fn wrong_type_error(
id: &v1::instance_spec::SpecKey,
kind: &str,
) -> MigrateError {
let msg =
format!("component {id} is not a {kind} in the source spec");
MigrateError::InstanceSpecsIncompatible(msg)
}

let v1::instance_spec::VersionedInstanceSpec::V0(mut source_spec) =
self.instance_spec;
for (id, comp) in replacements {
let Some(to_amend) = source_spec.components.get_mut(id) else {
return Err(MigrateError::InstanceSpecsIncompatible(format!(
"replacement component {id} not in source spec",
)));
};

match comp {
#[cfg(not(feature = "failure-injection"))]
ReplacementComponent::MigrationFailureInjector(_) => {
return Err(MigrateError::InstanceSpecsIncompatible(
format!(
"replacing migration failure injector {id} is \
impossible because the feature is compiled out"
),
));
}

#[cfg(feature = "failure-injection")]
ReplacementComponent::MigrationFailureInjector(comp) => {
let v1::instance_spec::Component::MigrationFailureInjector(
src,
) = to_amend
else {
return Err(wrong_type_error(
id,
"migration failure injector",
));
};

*src = comp.clone();
}
ReplacementComponent::CrucibleStorageBackend(comp) => {
let v1::instance_spec::Component::CrucibleStorageBackend(
src,
) = to_amend
else {
return Err(wrong_type_error(id, "crucible backend"));
};

*src = comp.clone();
}
ReplacementComponent::VirtioNetworkBackend(comp) => {
let v1::instance_spec::Component::VirtioNetworkBackend(src) =
to_amend
else {
return Err(wrong_type_error(id, "viona backend"));
};

*src = comp.clone();
}
}
}

let amended_spec =
source_spec.try_into().map_err(|e: ApiSpecError| {
MigrateError::PreambleParse(e.to_string())
})?;
self.instance_spec.into_amended_spec(replacements)?;

// TODO: Compare opaque blobs.

Expand Down
10 changes: 5 additions & 5 deletions bin/propolis-server/src/lib/migrate/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use propolis::migrate::{
MigrateCtx, MigrateStateError, Migrator, PayloadOutputs,
};
use propolis::vmm;
use propolis_api_types_versions::v1;
use slog::{debug, error, info, trace, warn};
use std::collections::HashMap;
use std::convert::TryInto;
Expand All @@ -25,6 +24,7 @@ use crate::migrate::memx;
use crate::migrate::preamble::Preamble;
use crate::migrate::probes;
use crate::migrate::protocol::Protocol;
use crate::migrate::types::VersionedInstanceSpec;
use crate::migrate::{codec, protocol};
use crate::migrate::{
Device, DevicePayload, MigrateError, MigratePhase, MigrateRole,
Expand Down Expand Up @@ -467,10 +467,10 @@ impl<T: MigrateConn> RonV0Runner<'_, T> {

async fn sync(&mut self) -> Result<(), MigrateError> {
self.update_state(MigrationState::Sync);
let preamble =
Preamble::new(v1::instance_spec::VersionedInstanceSpec::V0(
self.vm.lock_shared().await.instance_spec().clone().into(),
));
let versioned = VersionedInstanceSpec::from_spec(
self.vm.lock_shared().await.instance_spec(),
)?;
let preamble = Preamble::new(versioned);
let s = ron::ser::to_string(&preamble)
.map_err(codec::ProtocolError::from)?;
self.send_msg(codec::Message::Serialized(s)).await?;
Expand Down
Loading
Loading