Skip to content

Commit d2a671c

Browse files
committed
[sled-agent-config-reconciler] Flesh out ExternalDisks type
1 parent 638d3aa commit d2a671c

File tree

7 files changed

+1087
-144
lines changed

7 files changed

+1087
-144
lines changed

common/src/disk.rs

+3
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,9 @@ pub enum DiskManagementError {
518518
#[error("Disk requested by control plane, but not found on device")]
519519
NotFound,
520520

521+
#[error("Disk requested by control plane is an internal disk: {0}")]
522+
InternalDiskControlPlaneRequest(PhysicalDiskUuid),
523+
521524
#[error("Expected zpool UUID of {expected}, but saw {observed}")]
522525
ZpoolUuidMismatch { expected: ZpoolUuid, observed: ZpoolUuid },
523526

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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/2.0/.
4+
5+
//! Functionality common to both internal and external managed disk.
6+
7+
use sled_storage::disk::Disk;
8+
use sled_storage::disk::RawDisk;
9+
use slog::Logger;
10+
use slog::info;
11+
use slog::warn;
12+
13+
pub(crate) enum MaybeUpdatedDisk {
14+
Updated(Disk),
15+
Unchanged(Disk),
16+
}
17+
18+
pub(crate) fn update_properties_from_raw_disk(
19+
mut disk: Disk,
20+
raw_disk: &RawDisk,
21+
log: &Logger,
22+
) -> MaybeUpdatedDisk {
23+
if *raw_disk == RawDisk::from(disk.clone()) {
24+
return MaybeUpdatedDisk::Unchanged(disk);
25+
}
26+
27+
// The only property we expect to change is the firmware metadata. Update
28+
// that and check again; if they're still not equal, something weird is
29+
// going on. At least log a warning.
30+
disk.update_firmware_metadata(raw_disk);
31+
if *raw_disk == RawDisk::from(disk.clone()) {
32+
info!(
33+
log, "Updated disk firmware metadata";
34+
"firmware" => ?disk.firmware(),
35+
"identity" => ?disk.identity(),
36+
);
37+
} else {
38+
warn!(
39+
log,
40+
"Updated disk firmware metadata from raw disk properties, \
41+
but other properties are different!";
42+
"disk" => ?disk,
43+
"raw_disk" => ?*raw_disk,
44+
);
45+
}
46+
MaybeUpdatedDisk::Updated(disk)
47+
}

sled-agent/config-reconciler/src/external_disks.rs

-3
This file was deleted.

sled-agent/config-reconciler/src/internal_disks.rs

+11-29
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ use std::time::Duration;
4545
use tokio::sync::watch;
4646
use tokio::sync::watch::error::RecvError;
4747

48+
use crate::disks_common::MaybeUpdatedDisk;
49+
use crate::disks_common::update_properties_from_raw_disk;
4850
use crate::raw_disks::RawDiskWithId;
4951

5052
/// A thin wrapper around a [`watch::Receiver`] that presents a similar API.
@@ -728,36 +730,16 @@ impl InternalDisksTask {
728730
let existing_disk = disks
729731
.get(raw_disk.identity())
730732
.expect("disk should still be present");
731-
let existing_raw_disk =
732-
RawDisk::from(existing_disk.disk.clone());
733-
734-
if *raw_disk != existing_raw_disk {
735-
// The only property we expect to change is the firmware
736-
// metadata. Update that and check again; if they're still
737-
// not equal, something weird is going on. At least log a
738-
// warning.
739-
let mut updated_disk = existing_disk.clone();
740-
updated_disk.disk.update_firmware_metadata(&raw_disk);
741-
742-
if *raw_disk == RawDisk::from(updated_disk.disk.clone()) {
743-
info!(
744-
self.log, "Updated disk firmware metadata";
745-
"old" => ?updated_disk.firmware(),
746-
"new" => ?raw_disk.firmware(),
747-
"identity" => ?updated_disk.id(),
748-
);
749-
} else {
750-
warn!(
751-
self.log,
752-
"Updated disk firmware metadata, \
753-
but other disk properties are different!";
754-
"old" => ?existing_raw_disk,
755-
"new" => ?*raw_disk,
756-
);
733+
match update_properties_from_raw_disk(
734+
existing_disk.disk.clone(),
735+
&raw_disk,
736+
&self.log,
737+
) {
738+
MaybeUpdatedDisk::Updated(new_disk) => {
739+
disks.insert(InternalDisk::from(new_disk));
740+
changed = true;
757741
}
758-
759-
disks.insert(updated_disk);
760-
changed = true;
742+
MaybeUpdatedDisk::Unchanged(_) => (),
761743
}
762744
}
763745

sled-agent/config-reconciler/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
#![allow(dead_code)]
5151

5252
mod dataset_serialization_task;
53-
mod external_disks;
53+
mod disks_common;
5454
mod handle;
5555
mod internal_disks;
5656
mod ledger;

sled-agent/config-reconciler/src/reconciler_task.rs

+5-111
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,24 @@ use chrono::Utc;
99
use illumos_utils::dladm::EtherstubVnic;
1010
use illumos_utils::running_zone::RunCommandError;
1111
use illumos_utils::zpool::PathInPool;
12-
use illumos_utils::zpool::ZpoolName;
1312
use key_manager::StorageKeyRequester;
1413
use nexus_sled_agent_shared::inventory::OmicronSledConfig;
1514
use sled_agent_types::time_sync::TimeSync;
1615
use slog::Logger;
17-
use std::collections::BTreeSet;
1816
use std::sync::Arc;
1917
use std::time::Duration;
2018
use std::time::Instant;
2119
use tokio::sync::watch;
22-
use tokio::sync::watch::error::RecvError;
2320

2421
use crate::TimeSyncConfig;
2522
use crate::ledger::CurrentSledConfig;
2623
use crate::sled_agent_facilities::SledAgentFacilities;
2724

25+
mod external_disks;
26+
27+
pub use self::external_disks::CurrentlyManagedZpools;
28+
pub use self::external_disks::CurrentlyManagedZpoolsReceiver;
29+
2830
#[allow(clippy::too_many_arguments)]
2931
pub(crate) fn spawn<T: SledAgentFacilities>(
3032
key_requester: StorageKeyRequester,
@@ -133,114 +135,6 @@ pub enum TimeSyncError {
133135
FailedToParse { reason: &'static str, stdout: String },
134136
}
135137

136-
#[derive(Debug, Clone)]
137-
pub struct CurrentlyManagedZpoolsReceiver {
138-
inner: CurrentlyManagedZpoolsReceiverInner,
139-
}
140-
141-
#[derive(Debug, Clone)]
142-
enum CurrentlyManagedZpoolsReceiverInner {
143-
Real(watch::Receiver<Arc<CurrentlyManagedZpools>>),
144-
#[cfg(any(test, feature = "testing"))]
145-
FakeDynamic(watch::Receiver<BTreeSet<ZpoolName>>),
146-
#[cfg(any(test, feature = "testing"))]
147-
FakeStatic(BTreeSet<ZpoolName>),
148-
}
149-
150-
impl CurrentlyManagedZpoolsReceiver {
151-
#[cfg(any(test, feature = "testing"))]
152-
pub fn fake_dynamic(rx: watch::Receiver<BTreeSet<ZpoolName>>) -> Self {
153-
Self { inner: CurrentlyManagedZpoolsReceiverInner::FakeDynamic(rx) }
154-
}
155-
156-
#[cfg(any(test, feature = "testing"))]
157-
pub fn fake_static(zpools: impl Iterator<Item = ZpoolName>) -> Self {
158-
Self {
159-
inner: CurrentlyManagedZpoolsReceiverInner::FakeStatic(
160-
zpools.collect(),
161-
),
162-
}
163-
}
164-
165-
pub(crate) fn new(
166-
rx: watch::Receiver<Arc<CurrentlyManagedZpools>>,
167-
) -> Self {
168-
Self { inner: CurrentlyManagedZpoolsReceiverInner::Real(rx) }
169-
}
170-
171-
pub fn current(&self) -> Arc<CurrentlyManagedZpools> {
172-
match &self.inner {
173-
CurrentlyManagedZpoolsReceiverInner::Real(rx) => {
174-
Arc::clone(&*rx.borrow())
175-
}
176-
#[cfg(any(test, feature = "testing"))]
177-
CurrentlyManagedZpoolsReceiverInner::FakeDynamic(rx) => {
178-
Arc::new(CurrentlyManagedZpools(rx.borrow().clone()))
179-
}
180-
#[cfg(any(test, feature = "testing"))]
181-
CurrentlyManagedZpoolsReceiverInner::FakeStatic(zpools) => {
182-
Arc::new(CurrentlyManagedZpools(zpools.clone()))
183-
}
184-
}
185-
}
186-
187-
pub fn current_and_update(&mut self) -> Arc<CurrentlyManagedZpools> {
188-
match &mut self.inner {
189-
CurrentlyManagedZpoolsReceiverInner::Real(rx) => {
190-
Arc::clone(&*rx.borrow_and_update())
191-
}
192-
#[cfg(any(test, feature = "testing"))]
193-
CurrentlyManagedZpoolsReceiverInner::FakeDynamic(rx) => {
194-
Arc::new(CurrentlyManagedZpools(rx.borrow_and_update().clone()))
195-
}
196-
#[cfg(any(test, feature = "testing"))]
197-
CurrentlyManagedZpoolsReceiverInner::FakeStatic(zpools) => {
198-
Arc::new(CurrentlyManagedZpools(zpools.clone()))
199-
}
200-
}
201-
}
202-
203-
/// Wait for changes in the underlying watch channel.
204-
///
205-
/// Cancel-safe.
206-
pub async fn changed(&mut self) -> Result<(), RecvError> {
207-
match &mut self.inner {
208-
CurrentlyManagedZpoolsReceiverInner::Real(rx) => rx.changed().await,
209-
#[cfg(any(test, feature = "testing"))]
210-
CurrentlyManagedZpoolsReceiverInner::FakeDynamic(rx) => {
211-
rx.changed().await
212-
}
213-
#[cfg(any(test, feature = "testing"))]
214-
CurrentlyManagedZpoolsReceiverInner::FakeStatic(_) => {
215-
// Static set of zpools never changes
216-
std::future::pending().await
217-
}
218-
}
219-
}
220-
}
221-
222-
/// Set of currently managed zpools.
223-
///
224-
/// This handle should only be used to decide to _stop_ using a zpool (e.g., if
225-
/// a previously-launched zone is on a zpool that is no longer managed). It does
226-
/// not expose a means to list or choose from the currently-managed pools;
227-
/// instead, consumers should choose mounted datasets.
228-
///
229-
/// This level of abstraction even for "when to stop using a zpool" is probably
230-
/// wrong: if we choose a dataset on which to place a zone's root, we should
231-
/// shut that zone down if the _dataset_ goes away, not the zpool. For now we
232-
/// live with "assume the dataset bases we choose stick around as long as their
233-
/// parent zpool does".
234-
#[derive(Default, Debug, Clone)]
235-
pub struct CurrentlyManagedZpools(BTreeSet<ZpoolName>);
236-
237-
impl CurrentlyManagedZpools {
238-
/// Returns true if `zpool` is currently managed.
239-
pub fn contains(&self, zpool: &ZpoolName) -> bool {
240-
self.0.contains(zpool)
241-
}
242-
}
243-
244138
#[derive(Debug)]
245139
struct LatestReconcilerTaskResultInner {
246140
sled_config: OmicronSledConfig,

0 commit comments

Comments
 (0)