Skip to content

Commit 7d23864

Browse files
authored
Use instance spec compatibility checks in migration sync phase (#169)
Stash an instance's spec in its server context. During a migration, have the source send its spec to the target, and have the target use is_migration_compatible to check the specs for compatibility.
1 parent 747f092 commit 7d23864

File tree

5 files changed

+30
-71
lines changed

5 files changed

+30
-71
lines changed

bin/propolis-server/src/lib/migrate/destination.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use hyper::upgrade::Upgraded;
44
use propolis::common::GuestAddr;
55
use propolis::instance::MigrateRole;
66
use propolis::migrate::{MigrateStateError, Migrator};
7+
use propolis_client::instance_spec::MigrationCompatible;
78
use slog::{error, info, warn};
89
use std::io;
910
use std::sync::Arc;
@@ -86,12 +87,14 @@ impl DestinationProtocol {
8687
Err(MigrateError::UnexpectedMessage)
8788
}
8889
}?;
89-
info!(self.log(), "Src read Preamble: {:?}", preamble);
90-
// XXX: For demonstration purposes only.
91-
if preamble.vm_descr.vcpus != vec![0u32, 1, 2, 3] {
90+
info!(self.log(), "Destination read Preamble: {:?}", preamble);
91+
if !preamble
92+
.instance_spec
93+
.is_migration_compatible(&self.mctx.instance_spec)
94+
{
9295
error!(
9396
self.log(),
94-
"invalid CPU count in preamble ({:?})", preamble.vm_descr.vcpus
97+
"Source and destination instance specs incompatible"
9598
);
9699
return Err(MigrateError::InvalidInstanceState);
97100
}
@@ -216,6 +219,11 @@ impl DestinationProtocol {
216219

217220
let inv = self.mctx.instance.inv();
218221
for device in devices {
222+
info!(
223+
self.log(),
224+
"Applying state to device {}", device.instance_name
225+
);
226+
219227
let dev_ent =
220228
inv.get_by_name(&device.instance_name).ok_or_else(|| {
221229
MigrateError::UnknownDevice(device.instance_name.clone())

bin/propolis-server/src/lib/migrate/mod.rs

+11-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use propolis::{
88
instance::{Instance, MigratePhase, MigrateRole, State, TransitionError},
99
migrate::MigrateStateError,
1010
};
11-
use propolis_client::api::{self, MigrationState};
11+
use propolis_client::{
12+
api::{self, MigrationState},
13+
instance_spec::InstanceSpec,
14+
};
1215
use serde::{Deserialize, Serialize};
1316
use slog::{error, info, o};
1417
use thiserror::Error;
@@ -60,6 +63,9 @@ pub struct MigrateContext {
6063
/// A handle to the underlying propolis [`Instance`].
6164
instance: Arc<Instance>,
6265

66+
/// A copy of this instance's spec.
67+
instance_spec: InstanceSpec,
68+
6369
/// Async descriptor context for the migrate task to access machine state in async context.
6470
async_ctx: AsyncCtx,
6571

@@ -71,13 +77,15 @@ impl MigrateContext {
7177
fn new(
7278
migration_id: Uuid,
7379
instance: Arc<Instance>,
80+
instance_spec: InstanceSpec,
7481
log: slog::Logger,
7582
) -> MigrateContext {
7683
MigrateContext {
7784
migration_id,
7885
state: RwLock::new(MigrationState::Sync),
7986
async_ctx: instance.async_ctx(),
8087
instance,
88+
instance_spec,
8189
log,
8290
}
8391
}
@@ -311,6 +319,7 @@ pub async fn source_start(
311319
let migrate_context = Arc::new(MigrateContext::new(
312320
migration_id,
313321
context.instance.clone(),
322+
context.spec.clone(),
314323
log.clone(),
315324
));
316325
let mctx = migrate_context.clone();
@@ -434,6 +443,7 @@ pub async fn dest_initiate(
434443
let migrate_context = Arc::new(MigrateContext::new(
435444
migration_id,
436445
context.instance.clone(),
446+
context.spec.clone(),
437447
log.clone(),
438448
));
439449
let mctx = migrate_context.clone();
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,14 @@
1-
use propolis::instance::Instance;
1+
use propolis_client::instance_spec::InstanceSpec;
22
use serde::{Deserialize, Serialize};
33

4-
#[derive(Deserialize, Serialize, Debug)]
5-
pub(crate) enum MemType {
6-
RAM,
7-
ROM,
8-
Dev,
9-
Res,
10-
}
11-
12-
// Memory regions in the guest physical address space.
13-
#[derive(Deserialize, Serialize, Debug)]
14-
pub(crate) struct MemRegion {
15-
pub start: u64,
16-
pub end: u64,
17-
pub typ: MemType,
18-
}
19-
20-
// PCI vendor and device ID pairs.
21-
#[derive(Deserialize, Serialize, Debug)]
22-
pub(crate) struct PciId {
23-
pub vendor: u16,
24-
pub device: u16,
25-
}
26-
27-
// PCI Bus/Device/Function.
28-
#[derive(Deserialize, Serialize, Debug)]
29-
pub(crate) struct PciBdf {
30-
pub bus: u16,
31-
pub device: u8,
32-
pub function: u8,
33-
}
34-
35-
// PIO ranges associated with some device.
36-
#[derive(Deserialize, Serialize, Debug)]
37-
pub(crate) struct DevPorts {
38-
pub device: u32,
39-
pub ports: Vec<u16>,
40-
}
41-
42-
#[derive(Deserialize, Serialize, Debug)]
43-
pub(crate) struct VmDescr {
44-
pub vcpus: Vec<u32>, // APIC IDs.
45-
pub ioapics: Vec<u32>, // IOAPICs.
46-
pub mem: Vec<MemRegion>, // Start, end, type
47-
pub pci: Vec<(PciId, PciBdf)>, // Vendor/ID + BDF
48-
pub ports: Vec<DevPorts>,
49-
}
50-
51-
impl VmDescr {
52-
pub fn new(_instance: &Instance) -> VmDescr {
53-
// XXX: Just for demo purposes. We should get this from the instance.
54-
let vcpus = vec![0, 1, 2, 3];
55-
VmDescr {
56-
vcpus,
57-
ioapics: Vec::new(),
58-
mem: Vec::new(),
59-
pci: Vec::new(),
60-
ports: Vec::new(),
61-
}
62-
}
63-
}
64-
654
#[derive(Deserialize, Serialize, Debug)]
665
pub(crate) struct Preamble {
67-
pub vm_descr: VmDescr,
6+
pub instance_spec: InstanceSpec,
687
pub blobs: Vec<Vec<u8>>,
698
}
709

7110
impl Preamble {
72-
pub fn new(instance: &Instance) -> Preamble {
73-
Preamble { vm_descr: VmDescr::new(instance), blobs: Vec::new() }
11+
pub fn new(instance_spec: InstanceSpec) -> Preamble {
12+
Preamble { instance_spec, blobs: Vec::new() }
7413
}
7514
}

bin/propolis-server/src/lib/migrate/source.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ impl SourceProtocol {
8181

8282
async fn sync(&mut self) -> Result<(), MigrateError> {
8383
self.mctx.set_state(MigrationState::Sync).await;
84-
let preamble = Preamble::new(self.mctx.instance.as_ref());
84+
let preamble = Preamble::new(self.mctx.instance_spec.clone());
8585
let s = ron::ser::to_string(&preamble)
8686
.map_err(codec::ProtocolError::from)?;
8787
self.send_msg(codec::Message::Serialized(s)).await?;

bin/propolis-server/src/lib/server.rs

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ pub(crate) struct InstanceContext {
8787
// The instance, which may or may not be instantiated.
8888
pub instance: Arc<Instance>,
8989
pub properties: api::InstanceProperties,
90+
pub spec: instance_spec::InstanceSpec,
9091
serial: Option<Arc<Serial<LpcUart>>>,
9192
state_watcher: watch::Receiver<StateChange>,
9293
serial_task: Option<SerialTask>,
@@ -448,6 +449,7 @@ async fn instance_ensure(
448449
*context = Some(InstanceContext {
449450
instance: instance.clone(),
450451
properties,
452+
spec,
451453
serial: com1,
452454
state_watcher: rx,
453455
serial_task: None,

0 commit comments

Comments
 (0)