@@ -85,6 +85,7 @@ use tufaceous_artifact::ArtifactVersionError;
8585use tufaceous_lib:: assemble:: ArtifactManifest ;
8686
8787mod log_capture;
88+ pub mod test_utils;
8889
8990/// REPL state
9091#[ derive( Debug ) ]
@@ -261,6 +262,138 @@ impl ReconfiguratorSim {
261262
262263 Ok ( builder. build ( ) )
263264 }
265+
266+ fn load_example < F > (
267+ & mut self ,
268+ seed : Option < String > ,
269+ f : F ,
270+ ) -> anyhow:: Result < String >
271+ where
272+ F : FnOnce ( ExampleSystemBuilder ) -> anyhow:: Result < ExampleSystemBuilder > ,
273+ {
274+ let mut s = String :: new ( ) ;
275+ let mut state = self . current_state ( ) . to_mut ( ) ;
276+ if !state. system_mut ( ) . is_empty ( ) {
277+ bail ! (
278+ "changes made to simulated system: run `wipe system` before \
279+ loading"
280+ ) ;
281+ }
282+
283+ // Generate the example system.
284+ match seed {
285+ Some ( seed) => {
286+ // In this case, reset the RNG state to the provided seed.
287+ swriteln ! ( s, "setting new RNG seed: {}" , seed) ;
288+ state. rng_mut ( ) . set_seed ( seed) ;
289+ }
290+ None => {
291+ // In this case, use the existing RNG state.
292+ swriteln ! (
293+ s,
294+ "using existing RNG state (seed: {})" ,
295+ state. rng_mut( ) . seed( )
296+ ) ;
297+ }
298+ } ;
299+ let rng = state. rng_mut ( ) . next_example_rng ( ) ;
300+
301+ let builder = f ( ExampleSystemBuilder :: new_with_rng ( & self . log , rng)
302+ . nexus_count (
303+ state
304+ . config_mut ( )
305+ . num_nexus ( )
306+ . map_or ( NEXUS_REDUNDANCY , |n| n. into ( ) ) ,
307+ ) ) ?;
308+
309+ let ( example, blueprint) = builder. build ( ) ;
310+
311+ // Generate the internal and external DNS configs based on the blueprint.
312+ let sleds_by_id = make_sleds_by_id ( & example. system ) ?;
313+ let blueprint_nexus_generation =
314+ blueprint_active_nexus_generation ( & blueprint) ;
315+ let internal_dns = blueprint_internal_dns_config (
316+ & blueprint,
317+ & sleds_by_id,
318+ blueprint_nexus_generation,
319+ & Default :: default ( ) ,
320+ ) ?;
321+ let external_dns_zone_name =
322+ state. config_mut ( ) . external_dns_zone_name ( ) . to_owned ( ) ;
323+ let external_dns = blueprint_external_dns_config (
324+ & blueprint,
325+ state. config_mut ( ) . silo_names ( ) ,
326+ external_dns_zone_name,
327+ blueprint_nexus_generation,
328+ ) ;
329+
330+ let blueprint_id = blueprint. id ;
331+ let collection_id = example. collection . id ;
332+
333+ state
334+ . system_mut ( )
335+ . load_example ( example, blueprint, internal_dns, external_dns)
336+ . expect ( "already checked non-empty state above" ) ;
337+ self . commit_and_bump (
338+ "reconfigurator-cli load-example" . to_owned ( ) ,
339+ state,
340+ ) ;
341+
342+ Ok ( format ! (
343+ "loaded example system with:\n \
344+ - collection: {collection_id}\n \
345+ - blueprint: {blueprint_id}",
346+ ) )
347+ }
348+
349+ fn run_planner (
350+ & mut self ,
351+ parent_blueprint_id : BlueprintId ,
352+ collection_id : CollectionId ,
353+ ) -> anyhow:: Result < String > {
354+ let mut state = self . current_state ( ) . to_mut ( ) ;
355+ let rng = state. rng_mut ( ) . next_planner_rng ( ) ;
356+ let system = state. system_mut ( ) ;
357+
358+ let parent_blueprint = {
359+ let resolved = system. resolve_blueprint_id ( parent_blueprint_id) ;
360+ system. get_blueprint ( & resolved) ?
361+ } ;
362+
363+ let collection = {
364+ let resolved = system. resolve_collection_id ( collection_id) ?;
365+ system. get_collection ( & resolved) ?
366+ } ;
367+
368+ let creator = "reconfigurator-sim" ;
369+ let planning_input = self
370+ . planning_input ( parent_blueprint)
371+ . context ( "failed to construct planning input" ) ?;
372+ let planner = Planner :: new_based_on (
373+ self . log . clone ( ) ,
374+ parent_blueprint,
375+ & planning_input,
376+ creator,
377+ collection,
378+ rng,
379+ )
380+ . context ( "creating planner" ) ?;
381+
382+ let blueprint = planner. plan ( ) . context ( "generating blueprint" ) ?;
383+ let rv = format ! (
384+ "generated blueprint {} based on parent blueprint {}\n \
385+ blueprint source: {}",
386+ blueprint. id, parent_blueprint. id, blueprint. source,
387+ ) ;
388+ system. add_blueprint ( blueprint) ?;
389+
390+ self . commit_and_bump (
391+ "reconfigurator-cli blueprint-plan" . to_owned ( ) ,
392+ state,
393+ ) ;
394+
395+ Ok ( rv)
396+ }
264397}
265398
266399/// interactive REPL for exploring the planner
@@ -1756,7 +1889,7 @@ fn cmd_sled_set(
17561889 }
17571890 SledSetCommand :: OmicronConfig ( command) => {
17581891 let resolved_id =
1759- system. resolve_blueprint_id ( command. blueprint . into ( ) ) ? ;
1892+ system. resolve_blueprint_id ( command. blueprint . into ( ) ) ;
17601893 let blueprint = system. get_blueprint ( & resolved_id) ?;
17611894 let sled_cfg =
17621895 blueprint. sleds . get ( & sled_id) . with_context ( || {
@@ -2236,7 +2369,7 @@ fn cmd_blueprint_blippy(
22362369) -> anyhow:: Result < Option < String > > {
22372370 let state = sim. current_state ( ) ;
22382371 let resolved_id =
2239- state. system ( ) . resolve_blueprint_id ( args. blueprint_id . into ( ) ) ? ;
2372+ state. system ( ) . resolve_blueprint_id ( args. blueprint_id . into ( ) ) ;
22402373 let blueprint = state. system ( ) . get_blueprint ( & resolved_id) ?;
22412374 let planning_input = sim
22422375 . planning_input ( blueprint)
@@ -2250,24 +2383,19 @@ fn cmd_blueprint_plan(
22502383 sim : & mut ReconfiguratorSim ,
22512384 args : BlueprintPlanArgs ,
22522385) -> anyhow:: Result < Option < String > > {
2253- let mut state = sim. current_state ( ) . to_mut ( ) ;
2254- let rng = state. rng_mut ( ) . next_planner_rng ( ) ;
2255- let system = state. system_mut ( ) ;
2386+ let state = sim. current_state ( ) ;
2387+ let system = state. system ( ) ;
22562388
2257- let parent_blueprint_id =
2258- system. resolve_blueprint_id ( args. parent_blueprint_id . into ( ) ) ?;
2259- let parent_blueprint = system. get_blueprint ( & parent_blueprint_id) ?;
2260- let collection = match args. collection_id {
2261- Some ( collection_id) => {
2262- let resolved =
2263- system. resolve_collection_id ( collection_id. into ( ) ) ?;
2264- system. get_collection ( & resolved) ?
2265- }
2389+ let parent_blueprint_id = args. parent_blueprint_id . into ( ) ;
2390+ let collection_id = match args. collection_id {
2391+ Some ( collection_id) => collection_id. into ( ) ,
22662392 None => {
22672393 let mut all_collections_iter = system. all_collections ( ) ;
22682394 match all_collections_iter. len ( ) {
22692395 0 => bail ! ( "cannot plan blueprint with no loaded collections" ) ,
2270- 1 => all_collections_iter. next ( ) . expect ( "iter length is 1" ) ,
2396+ 1 => CollectionId :: Id (
2397+ all_collections_iter. next ( ) . expect ( "iter length is 1" ) . id ,
2398+ ) ,
22712399 _ => bail ! (
22722400 "blueprint-plan: must specify collection ID (one of {:?})" ,
22732401 all_collections_iter. map( |c| c. id) . join( ", " )
@@ -2276,31 +2404,7 @@ fn cmd_blueprint_plan(
22762404 }
22772405 } ;
22782406
2279- let creator = "reconfigurator-sim" ;
2280- let planning_input = sim
2281- . planning_input ( parent_blueprint)
2282- . context ( "failed to construct planning input" ) ?;
2283- let planner = Planner :: new_based_on (
2284- sim. log . clone ( ) ,
2285- parent_blueprint,
2286- & planning_input,
2287- creator,
2288- collection,
2289- rng,
2290- )
2291- . context ( "creating planner" ) ?;
2292-
2293- let blueprint = planner. plan ( ) . context ( "generating blueprint" ) ?;
2294- let rv = format ! (
2295- "generated blueprint {} based on parent blueprint {}\n \
2296- blueprint source: {}",
2297- blueprint. id, parent_blueprint. id, blueprint. source,
2298- ) ;
2299- system. add_blueprint ( blueprint) ?;
2300-
2301- sim. commit_and_bump ( "reconfigurator-cli blueprint-plan" . to_owned ( ) , state) ;
2302-
2303- Ok ( Some ( rv) )
2407+ sim. run_planner ( parent_blueprint_id, collection_id) . map ( Some )
23042408}
23052409
23062410fn cmd_blueprint_edit (
@@ -2311,7 +2415,7 @@ fn cmd_blueprint_edit(
23112415 let rng = state. rng_mut ( ) . next_planner_rng ( ) ;
23122416 let system = state. system_mut ( ) ;
23132417
2314- let resolved_id = system. resolve_blueprint_id ( args. blueprint_id . into ( ) ) ? ;
2418+ let resolved_id = system. resolve_blueprint_id ( args. blueprint_id . into ( ) ) ;
23152419 let blueprint = system. get_blueprint ( & resolved_id) ?;
23162420 let creator = args. creator . as_deref ( ) . unwrap_or ( "reconfigurator-cli" ) ;
23172421 let planning_input = sim
@@ -2779,7 +2883,7 @@ fn cmd_blueprint_history(
27792883
27802884 let state = sim. current_state ( ) ;
27812885 let system = state. system ( ) ;
2782- let resolved_id = system. resolve_blueprint_id ( blueprint_id. into ( ) ) ? ;
2886+ let resolved_id = system. resolve_blueprint_id ( blueprint_id. into ( ) ) ;
27832887 let mut blueprint = system. get_blueprint ( & resolved_id) ?;
27842888
27852889 // We want to print the output in logical order, but in order to construct
@@ -2852,8 +2956,7 @@ fn cmd_blueprint_save(
28522956 let blueprint_id = args. blueprint_id ;
28532957
28542958 let state = sim. current_state ( ) ;
2855- let resolved_id =
2856- state. system ( ) . resolve_blueprint_id ( blueprint_id. into ( ) ) ?;
2959+ let resolved_id = state. system ( ) . resolve_blueprint_id ( blueprint_id. into ( ) ) ;
28572960 let blueprint = state. system ( ) . get_blueprint ( & resolved_id) ?;
28582961
28592962 let output_path = & args. filename ;
@@ -3362,86 +3465,21 @@ fn cmd_load_example(
33623465 sim : & mut ReconfiguratorSim ,
33633466 args : LoadExampleArgs ,
33643467) -> anyhow:: Result < Option < String > > {
3365- let mut s = String :: new ( ) ;
3366- let mut state = sim. current_state ( ) . to_mut ( ) ;
3367- if !state. system_mut ( ) . is_empty ( ) {
3368- bail ! (
3369- "changes made to simulated system: run `wipe system` before \
3370- loading"
3371- ) ;
3372- }
3373-
3374- // Generate the example system.
3375- match args. seed {
3376- Some ( seed) => {
3377- // In this case, reset the RNG state to the provided seed.
3378- swriteln ! ( s, "setting new RNG seed: {}" , seed) ;
3379- state. rng_mut ( ) . set_seed ( seed) ;
3380- }
3381- None => {
3382- // In this case, use the existing RNG state.
3383- swriteln ! (
3384- s,
3385- "using existing RNG state (seed: {})" ,
3386- state. rng_mut( ) . seed( )
3387- ) ;
3388- }
3389- } ;
3390- let rng = state. rng_mut ( ) . next_example_rng ( ) ;
3391-
3392- let mut builder = ExampleSystemBuilder :: new_with_rng ( & sim. log , rng)
3393- . nsleds ( args. nsleds )
3394- . ndisks_per_sled ( args. ndisks_per_sled )
3395- . nexus_count (
3396- state
3397- . config_mut ( )
3398- . num_nexus ( )
3399- . map_or ( NEXUS_REDUNDANCY , |n| n. into ( ) ) ,
3400- )
3401- . external_dns_count ( 3 )
3402- . context ( "invalid external DNS zone count" ) ?
3403- . create_disks_in_blueprint ( !args. no_disks_in_blueprint ) ;
3404- for sled_policy in args. sled_policy {
3405- builder = builder
3406- . with_sled_policy ( sled_policy. index , sled_policy. policy )
3407- . context ( "setting sled policy" ) ?;
3408- }
3409-
3410- let ( example, blueprint) = builder. build ( ) ;
3411-
3412- // Generate the internal and external DNS configs based on the blueprint.
3413- let sleds_by_id = make_sleds_by_id ( & example. system ) ?;
3414- let blueprint_nexus_generation =
3415- blueprint_active_nexus_generation ( & blueprint) ;
3416- let internal_dns = blueprint_internal_dns_config (
3417- & blueprint,
3418- & sleds_by_id,
3419- blueprint_nexus_generation,
3420- & Default :: default ( ) ,
3421- ) ?;
3422- let external_dns_zone_name =
3423- state. config_mut ( ) . external_dns_zone_name ( ) . to_owned ( ) ;
3424- let external_dns = blueprint_external_dns_config (
3425- & blueprint,
3426- state. config_mut ( ) . silo_names ( ) ,
3427- external_dns_zone_name,
3428- blueprint_nexus_generation,
3429- ) ;
3430-
3431- let blueprint_id = blueprint. id ;
3432- let collection_id = example. collection . id ;
3433-
3434- state
3435- . system_mut ( )
3436- . load_example ( example, blueprint, internal_dns, external_dns)
3437- . expect ( "already checked non-empty state above" ) ;
3438- sim. commit_and_bump ( "reconfigurator-cli load-example" . to_owned ( ) , state) ;
3439-
3440- Ok ( Some ( format ! (
3441- "loaded example system with:\n \
3442- - collection: {collection_id}\n \
3443- - blueprint: {blueprint_id}",
3444- ) ) )
3468+ sim. load_example ( args. seed , |builder| {
3469+ let mut builder = builder
3470+ . nsleds ( args. nsleds )
3471+ . ndisks_per_sled ( args. ndisks_per_sled )
3472+ . external_dns_count ( 3 )
3473+ . context ( "invalid external DNS zone count" ) ?
3474+ . create_disks_in_blueprint ( !args. no_disks_in_blueprint ) ;
3475+ for sled_policy in args. sled_policy {
3476+ builder = builder
3477+ . with_sled_policy ( sled_policy. index , sled_policy. policy )
3478+ . context ( "setting sled policy" ) ?;
3479+ }
3480+ Ok ( builder)
3481+ } )
3482+ . map ( Some )
34453483}
34463484
34473485fn cmd_file_contents ( args : FileContentsArgs ) -> anyhow:: Result < Option < String > > {
0 commit comments