Skip to content

Commit c8716e2

Browse files
authored
assembly of reconfigurator state should have limits (#9197)
1 parent d74f5e3 commit c8716e2

File tree

5 files changed

+67
-9
lines changed

5 files changed

+67
-9
lines changed

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

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ enum ReconfiguratorCommands {
6565

6666
#[derive(Debug, Args, Clone)]
6767
struct ExportArgs {
68+
/// maximum number of blueprints to save
69+
#[clap(long, default_value_t = 1000)]
70+
nmax_blueprints: usize,
71+
6872
/// where to save the output
6973
output_file: Utf8PathBuf,
7074
}
@@ -147,21 +151,30 @@ async fn cmd_reconfigurator_export(
147151
) -> anyhow::Result<UnstableReconfiguratorState> {
148152
// See Nexus::blueprint_planning_context().
149153
eprint!("assembling reconfigurator state ... ");
154+
let limit = export_args.nmax_blueprints;
150155
let state = nexus_reconfigurator_preparation::reconfigurator_state_load(
151-
opctx, datastore,
156+
opctx, datastore, limit,
152157
)
153158
.await?;
154159
eprintln!("done");
155160

161+
if state.blueprints.len() >= limit {
162+
eprintln!(
163+
"warning: reached limit of {limit} while fetching blueprints"
164+
);
165+
eprintln!("warning: saving only the most recent {limit}");
166+
}
167+
156168
let output_path = &export_args.output_file;
169+
eprint!("saving to {} ... ", output_path);
157170
let file = std::fs::OpenOptions::new()
158171
.create_new(true)
159172
.write(true)
160173
.open(&output_path)
161174
.with_context(|| format!("open {:?}", output_path))?;
162175
serde_json::to_writer_pretty(&file, &state)
163176
.with_context(|| format!("write {:?}", output_path))?;
164-
eprintln!("wrote {}", output_path);
177+
eprintln!("done");
165178
Ok(state)
166179
}
167180

@@ -199,7 +212,7 @@ async fn cmd_reconfigurator_archive(
199212

200213
let mut ndeleted = 0;
201214

202-
eprintln!("removing non-target blueprints ...");
215+
eprintln!("removing saved, non-target blueprints ...");
203216
for blueprint in &saved_state.blueprints {
204217
if blueprint.id == target_blueprint_id {
205218
continue;
@@ -245,6 +258,15 @@ async fn cmd_reconfigurator_archive(
245258
eprintln!("done ({ndeleted} blueprint{plural} deleted)",);
246259
}
247260

261+
if saved_state.blueprints.len() >= archive_args.nmax_blueprints {
262+
eprintln!(
263+
"warning: Only tried deleting the most recent {} blueprints\n\
264+
warning: because that's all that was fetched and saved.\n\
265+
warning: You may want to run this tool again to archive more.",
266+
saved_state.blueprints.len(),
267+
);
268+
}
269+
248270
Ok(())
249271
}
250272

dev-tools/omdb/tests/successes.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1806,7 +1806,7 @@ stderr:
18061806
note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=disable
18071807
note: database schema version matches expected (<redacted database version>)
18081808
assembling reconfigurator state ... done
1809-
wrote <TMP_PATH_REDACTED>
1809+
saving to <TMP_PATH_REDACTED> ... done
18101810
=============================================
18111811
EXECUTING COMMAND: omdb ["--destructive", "db", "db-metadata", "force-mark-nexus-quiesced", "--skip-confirmation", "--skip-blueprint-validation", "..........<REDACTED_UUID>..........."]
18121812
termination: Exited(0)

nexus/db-queries/src/db/datastore/inventory.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4078,6 +4078,29 @@ impl DataStore {
40784078
internal_dns_generation_status,
40794079
})
40804080
}
4081+
4082+
pub async fn inventory_collections_latest(
4083+
&self,
4084+
opctx: &OpContext,
4085+
count: u8,
4086+
) -> anyhow::Result<Vec<InvCollection>> {
4087+
let limit: i64 = i64::from(count);
4088+
let conn = self
4089+
.pool_connection_authorized(opctx)
4090+
.await
4091+
.context("getting connection")?;
4092+
4093+
use nexus_db_schema::schema::inv_collection::dsl;
4094+
let collections = dsl::inv_collection
4095+
.select(InvCollection::as_select())
4096+
.order_by(dsl::time_started.desc())
4097+
.limit(limit)
4098+
.load_async(&*conn)
4099+
.await
4100+
.context("failed to list collections")?;
4101+
4102+
Ok(collections)
4103+
}
40814104
}
40824105

40834106
#[derive(Debug)]

nexus/reconfigurator/cli-integration-tests/tests/integration/blueprint_edit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ async fn test_blueprint_edit(cptestctx: &ControlPlaneTestContext) {
116116

117117
// Assemble state that we can load into reconfigurator-cli.
118118
let state1 = nexus_reconfigurator_preparation::reconfigurator_state_load(
119-
&opctx, datastore,
119+
&opctx, datastore, 20,
120120
)
121121
.await
122122
.expect("failed to assemble reconfigurator state");

nexus/reconfigurator/preparation/src/lib.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ use nexus_db_model::Generation;
1212
use nexus_db_queries::context::OpContext;
1313
use nexus_db_queries::db::DataStore;
1414
use nexus_db_queries::db::datastore::DataStoreDnsTest;
15-
use nexus_db_queries::db::datastore::DataStoreInventoryTest;
1615
use nexus_db_queries::db::datastore::Discoverability;
1716
use nexus_db_queries::db::datastore::SQL_BATCH_SIZE;
1817
use nexus_db_queries::db::pagination::Paginator;
@@ -57,6 +56,7 @@ use omicron_uuid_kinds::OmicronZoneUuid;
5756
use slog::Logger;
5857
use slog::error;
5958
use slog_error_chain::InlineErrorChain;
59+
use std::cmp::Reverse;
6060
use std::collections::BTreeMap;
6161
use std::collections::BTreeSet;
6262

@@ -406,12 +406,13 @@ async fn fetch_all_service_ip_pool_ranges(
406406
Ok(ranges)
407407
}
408408

409-
/// Loads state for import into `reconfigurator-cli`
409+
/// Loads state for debugging or import into `reconfigurator-cli`
410410
///
411-
/// This is only to be used in omdb or tests.
411+
/// This is used in omdb, tests, and in Nexus to collect support bundles
412412
pub async fn reconfigurator_state_load(
413413
opctx: &OpContext,
414414
datastore: &DataStore,
415+
nmax_blueprints: usize,
415416
) -> Result<UnstableReconfiguratorState, anyhow::Error> {
416417
opctx.check_complex_operations_allowed()?;
417418
let planner_config = datastore
@@ -420,8 +421,11 @@ pub async fn reconfigurator_state_load(
420421
.map_or_else(PlannerConfig::default, |c| c.config.planner_config);
421422
let planning_input =
422423
PlanningInputFromDb::assemble(opctx, datastore, planner_config).await?;
424+
425+
// We'll grab the most recent several inventory collections.
426+
const NCOLLECTIONS: u8 = 5;
423427
let collection_ids = datastore
424-
.inventory_collections()
428+
.inventory_collections_latest(opctx, NCOLLECTIONS)
425429
.await
426430
.context("listing collections")?
427431
.into_iter()
@@ -439,11 +443,13 @@ pub async fn reconfigurator_state_load(
439443
.collect::<Vec<Collection>>()
440444
.await;
441445

446+
// Grab the latest target blueprint.
442447
let target_blueprint = datastore
443448
.blueprint_target_get_current(opctx)
444449
.await
445450
.context("failed to read current target blueprint")?;
446451

452+
// Paginate through the list of all blueprints.
447453
let mut blueprint_ids = Vec::new();
448454
let mut paginator = Paginator::new(
449455
SQL_BATCH_SIZE,
@@ -460,6 +466,13 @@ pub async fn reconfigurator_state_load(
460466
blueprint_ids.extend(batch.into_iter());
461467
}
462468

469+
// We'll only grab the most recent blueprints that fit within the limit that
470+
// we were given. This is a heuristic intended to grab what's most likely
471+
// to be useful even when the system has a large number of blueprints. But
472+
// the intent is that callers provide a limit that should be large enough to
473+
// cover everything.
474+
blueprint_ids.sort_by_key(|bpm| Reverse(bpm.time_created));
475+
let blueprint_ids = blueprint_ids.into_iter().take(nmax_blueprints);
463476
let blueprints = futures::stream::iter(blueprint_ids)
464477
.filter_map(|bpm| async move {
465478
let blueprint_id = bpm.id.into_untyped_uuid();

0 commit comments

Comments
 (0)