Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 13 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ zone = { version = "0.3.1", default-features = false, features = ["async"] }
# the kinds). However, uses of omicron-uuid-kinds _within omicron_ will have
# std and the other features enabled because they'll refer to it via
# omicron-uuid-kinds.workspace = true.
newtype-uuid = { version = "1.3.1", default-features = false }
newtype-uuid = { version = "1.3.2", default-features = false }
newtype-uuid-macros = "0.1.0"
omicron-uuid-kinds = { path = "uuid-kinds", features = ["serde", "schemars08", "uuid-v4"] }

Expand Down
88 changes: 86 additions & 2 deletions dev-tools/reconfigurator-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ use nexus_reconfigurator_planning::planner::Planner;
use nexus_reconfigurator_planning::system::{
RotStateOverrides, SledBuilder, SledInventoryVisibility, SystemDescription,
};
use nexus_reconfigurator_simulation::{BlueprintId, CollectionId, SimState};
use nexus_reconfigurator_simulation::{
BlueprintId, CollectionId, GraphRenderOptions, SimState,
};
use nexus_reconfigurator_simulation::{SimStateBuilder, SimTufRepoSource};
use nexus_reconfigurator_simulation::{SimTufRepoDescription, Simulator};
use nexus_sled_agent_shared::inventory::ZoneKind;
Expand Down Expand Up @@ -322,6 +324,8 @@ fn process_command(
Commands::LoadExample(args) => cmd_load_example(sim, args),
Commands::FileContents(args) => cmd_file_contents(args),
Commands::Save(args) => cmd_save(sim, args),
Commands::State(StateArgs::Log(args)) => cmd_state_log(sim, args),
Commands::State(StateArgs::Switch(args)) => cmd_state_switch(sim, args),
Commands::Wipe(args) => cmd_wipe(sim, args),
};

Expand Down Expand Up @@ -420,6 +424,9 @@ enum Commands {
LoadExample(LoadExampleArgs),
/// show information about what's in a saved file
FileContents(FileContentsArgs),
/// state-related commands
#[command(flatten)]
State(StateArgs),
/// reset the state of the REPL
Wipe(WipeArgs),
}
Expand Down Expand Up @@ -1432,6 +1439,40 @@ struct SaveArgs {
filename: Utf8PathBuf,
}

#[derive(Debug, Subcommand)]
enum StateArgs {
/// display a log of simulator states
///
/// Shows the history of states from the current state back to the root.
Log(StateLogArgs),
/// switch to a different state
///
/// Changes the current working state to the specified state. All subsequent
/// commands will operate from this state.
Switch(StateSwitchArgs),
}

#[derive(Debug, Args)]
struct StateLogArgs {
/// Starting state ID (defaults to current state)
#[clap(long)]
from: Option<ReconfiguratorSimUuid>,

/// Limit number of states to display
#[clap(long, short = 'n')]
limit: Option<usize>,

/// Show changes in each state (verbose mode)
#[clap(long, short = 'v')]
verbose: bool,
}

#[derive(Debug, Args)]
struct StateSwitchArgs {
/// The state ID or unique prefix to switch to
state_id: String,
}

#[derive(Debug, Args)]
struct WipeArgs {
/// What to wipe
Expand Down Expand Up @@ -2765,6 +2806,49 @@ fn cmd_save(
)))
}

fn cmd_state_log(
sim: &mut ReconfiguratorSim,
args: StateLogArgs,
) -> anyhow::Result<Option<String>> {
let StateLogArgs { from, limit, verbose } = args;

// Build rendering options.
let options = GraphRenderOptions::new(sim.current)
.with_verbose(verbose)
.with_limit(limit)
.with_from(from);

// Render the graph.
let output = sim.sim.render_graph(&options);

Ok(Some(output))
}

fn cmd_state_switch(
sim: &mut ReconfiguratorSim,
args: StateSwitchArgs,
) -> anyhow::Result<Option<String>> {
// Try parsing as a full UUID first, then fall back to prefix matching.
let target_id = match args.state_id.parse::<ReconfiguratorSimUuid>() {
Ok(id) => id,
Err(_) => sim.sim.get_state_by_prefix(&args.state_id)?,
};

let state = sim
.sim
.get_state(target_id)
.ok_or_else(|| anyhow!("state {} not found", target_id))?;

sim.current = target_id;

Ok(Some(format!(
"switched to state {} (generation {}): {}",
target_id,
state.generation(),
state.description()
)))
}

fn cmd_wipe(
sim: &mut ReconfiguratorSim,
args: WipeArgs,
Expand All @@ -2776,7 +2860,7 @@ fn cmd_wipe(
state.config_mut().wipe();
state.rng_mut().reset_state();
format!(
"- wiped system, reconfigurator-sim config, and RNG state\n
"- wiped system, reconfigurator-sim config, and RNG state\n\
- reset seed to {}",
state.rng_mut().seed()
)
Expand Down
31 changes: 31 additions & 0 deletions dev-tools/reconfigurator-cli/tests/input/cmds-example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,34 @@ set planner-config --add-zones-with-mupdate-override true
# Sled index out of bounds, will error out.
wipe all
load-example --seed test-basic --nsleds 3 --sled-policy 3:non-provisionable

log
log --verbose

# Switch states with a full UUID.
switch 4d8d6725-1ae3-4f88-b9cb-a34db82d7439
log -n 3

# Switch states with a unique prefix.
switch 860f
log -n 3

# Switch states with an ambiguous prefix (should fail).
switch 4

# Switch states with a non-existent prefix (should fail).
switch zzzz

# Make a new branch.
sled-add
sled-add

# Make an additional branch based on the previous branch.
switch 4d8d6725
sled-add

# Go back to the previous branch.
switch 5c53cf4b

log
log --verbose
Loading
Loading