@@ -507,9 +507,16 @@ pub struct BlueprintBuilder<'a> {
507507 /// The ID that the completed blueprint will have
508508 new_blueprint_id : BlueprintUuid ,
509509
510+ // `sled_editors` contains only commissioned sleds. We still carry forward
511+ // any decommissioned sleds (that were either already decommissioned in
512+ // `parent_blueprint`, or that we ourselves decommissioned), but they're
513+ // stored separately in `decommissioned_sleds` to avoid the possibility that
514+ // we try to make additional changes after decommissioning.
515+ sled_editors : BTreeMap < SledUuid , SledEditor > ,
516+ decommissioned_sleds : BTreeMap < SledUuid , EditedSled > ,
517+
510518 // These fields will become part of the final blueprint. See the
511519 // corresponding fields in `Blueprint`.
512- sled_editors : BTreeMap < SledUuid , SledEditor > ,
513520 cockroachdb_setting_preserve_downgrade : CockroachDbPreserveDowngrade ,
514521 cockroachdb_fingerprint : String ,
515522 target_release_minimum_generation : Generation ,
@@ -575,14 +582,28 @@ impl<'a> BlueprintBuilder<'a> {
575582 "parent_id" => parent_blueprint. id. to_string( ) ,
576583 ) ) ;
577584
578- // Convert our parent blueprint's sled configs into `SledEditor`s.
585+ // Convert our parent blueprint's sled configs into `SledEditor` (for
586+ // commissioned sleds) or `EditedSled`s (with zero edits made, for
587+ // decommissioned sleds).
579588 let mut sled_editors = BTreeMap :: new ( ) ;
589+ let mut decommissioned_sleds = BTreeMap :: new ( ) ;
580590 for ( sled_id, sled_cfg) in & parent_blueprint. sleds {
581- let editor = SledEditor :: for_existing ( sled_cfg. clone ( ) )
582- . with_context ( || {
583- format ! ( "failed to construct SledEditor for sled {sled_id}" )
584- } ) ?;
585- sled_editors. insert ( * sled_id, editor) ;
591+ match sled_cfg. state {
592+ SledState :: Active => {
593+ let editor = SledEditor :: for_existing ( sled_cfg. clone ( ) )
594+ . with_context ( || {
595+ format ! (
596+ "failed to construct SledEditor \
597+ for sled {sled_id}"
598+ )
599+ } ) ?;
600+ sled_editors. insert ( * sled_id, editor) ;
601+ }
602+ SledState :: Decommissioned => {
603+ let edited = EditedSled :: new ( sled_cfg. clone ( ) ) ;
604+ decommissioned_sleds. insert ( * sled_id, edited) ;
605+ }
606+ }
586607 }
587608
588609 // Copy the Oximeter read policy from our parent blueprint so we can
@@ -601,6 +622,7 @@ impl<'a> BlueprintBuilder<'a> {
601622 oximeter_read_policy,
602623 new_blueprint_id : rng. next_blueprint ( ) ,
603624 sled_editors,
625+ decommissioned_sleds,
604626 cockroachdb_setting_preserve_downgrade : parent_blueprint
605627 . cockroachdb_setting_preserve_downgrade ,
606628 cockroachdb_fingerprint : parent_blueprint
@@ -695,7 +717,7 @@ impl<'a> BlueprintBuilder<'a> {
695717 let any_sled_subnet = self
696718 . sled_editors
697719 . values ( )
698- . filter_map ( |editor| editor. subnet ( ) )
720+ . map ( |editor| editor. subnet ( ) )
699721 . next ( )
700722 . ok_or ( Error :: RackSubnetUnknownNoSleds ) ?;
701723 let rack_subnet = ReservedRackSubnet :: from_subnet ( any_sled_subnet) ;
@@ -731,12 +753,7 @@ impl<'a> BlueprintBuilder<'a> {
731753 pub fn current_commissioned_sleds (
732754 & self ,
733755 ) -> impl Iterator < Item = SledUuid > + ' _ {
734- self . sled_editors . iter ( ) . filter_map ( |( sled_id, editor) | {
735- match editor. state ( ) {
736- SledState :: Active => Some ( * sled_id) ,
737- SledState :: Decommissioned => None ,
738- }
739- } )
756+ self . sled_editors . keys ( ) . copied ( )
740757 }
741758
742759 /// Iterate over the in-service [`BlueprintZoneConfig`] instances on a
@@ -873,12 +890,17 @@ impl<'a> BlueprintBuilder<'a> {
873890 pub fn build ( self , source : BlueprintSource ) -> Blueprint {
874891 let blueprint_id = self . new_blueprint_id ( ) ;
875892
876- // Collect the Omicron zones config for all sleds, including sleds that
877- // are no longer in service and need expungement work .
893+ // Collect the Omicron zones config for all sleds, including any
894+ // decommissioned sleds, which we continue to carry forward .
878895 let mut sleds = BTreeMap :: new ( ) ;
879- for ( sled_id, editor) in self . sled_editors {
880- let EditedSled { config, edit_counts, scalar_edits } =
881- editor. finalize ( ) ;
896+ let commissioned_sleds = self
897+ . sled_editors
898+ . into_iter ( )
899+ . map ( |( id, editor) | ( id, editor. finalize ( ) ) ) ;
900+ let all_sleds = commissioned_sleds. chain ( self . decommissioned_sleds ) ;
901+
902+ for ( sled_id, edited_sled) in all_sleds {
903+ let EditedSled { config, edit_counts, scalar_edits } = edited_sled;
882904 sleds. insert ( sled_id, config) ;
883905 if edit_counts. has_nonzero_counts ( ) || scalar_edits. has_edits ( ) {
884906 let EditedSledScalarEdits {
@@ -892,8 +914,10 @@ impl<'a> BlueprintBuilder<'a> {
892914 "disk_edits" => ?edit_counts. disks,
893915 "dataset_edits" => ?edit_counts. datasets,
894916 "zone_edits" => ?edit_counts. zones,
895- "debug_force_generation_bump" => debug_force_generation_bump,
896- "remove_mupdate_override_modified" => remove_mupdate_override,
917+ "debug_force_generation_bump" =>
918+ debug_force_generation_bump,
919+ "remove_mupdate_override_modified" =>
920+ remove_mupdate_override,
897921 ) ;
898922 } else {
899923 debug ! (
@@ -936,27 +960,42 @@ impl<'a> BlueprintBuilder<'a> {
936960 & self ,
937961 sled_id : SledUuid ,
938962 ) -> Result < SledState , Error > {
939- let editor = self . sled_editors . get ( & sled_id) . ok_or_else ( || {
940- Error :: Planner ( anyhow ! (
963+ if self . sled_editors . contains_key ( & sled_id) {
964+ Ok ( SledState :: Active )
965+ } else if self . decommissioned_sleds . contains_key ( & sled_id) {
966+ Ok ( SledState :: Decommissioned )
967+ } else {
968+ Err ( Error :: Planner ( anyhow ! (
941969 "tried to get sled state for unknown sled {sled_id}"
942- ) )
943- } ) ?;
944- Ok ( editor. state ( ) )
970+ ) ) )
971+ }
945972 }
946973
947974 /// Set the desired state of the given sled.
975+ ///
976+ /// Fails if the sled is not in the current set of commissioned sleds (i.e.,
977+ /// the sled doesn't exist or exists but is already decommissioned).
948978 pub fn set_sled_decommissioned (
949979 & mut self ,
950980 sled_id : SledUuid ,
951981 ) -> Result < ( ) , Error > {
952- let editor = self . sled_editors . get_mut ( & sled_id) . ok_or_else ( || {
982+ let editor = self . sled_editors . remove ( & sled_id) . ok_or_else ( || {
953983 Error :: Planner ( anyhow ! (
954984 "tried to set sled state for unknown sled {sled_id}"
955985 ) )
956986 } ) ?;
957- editor
958- . decommission ( )
959- . map_err ( |err| Error :: SledEditError { sled_id, err } )
987+ match editor. decommission ( ) {
988+ Ok ( edited) => {
989+ self . decommissioned_sleds . insert ( sled_id, edited) ;
990+ Ok ( ( ) )
991+ }
992+ Err ( ( editor, err) ) => {
993+ // We failed to decommission the sled but already removed it
994+ // from `sled_editors`; put it back before returning.
995+ self . sled_editors . insert ( sled_id, editor) ;
996+ Err ( Error :: SledEditError { sled_id, err } )
997+ }
998+ }
960999 }
9611000
9621001 /// This is a short human-readable string summarizing the changes reflected
@@ -2074,9 +2113,8 @@ impl<'a> BlueprintBuilder<'a> {
20742113 "tried to change image of zone on unknown sled {sled_id}"
20752114 ) )
20762115 } ) ?;
2077- editor
2078- . set_host_phase_2 ( host_phase_2)
2079- . map_err ( |err| Error :: SledEditError { sled_id, err } )
2116+ editor. set_host_phase_2 ( host_phase_2) ;
2117+ Ok ( ( ) )
20802118 }
20812119
20822120 pub fn sled_set_host_phase_2_slot (
@@ -2090,9 +2128,8 @@ impl<'a> BlueprintBuilder<'a> {
20902128 "tried to change image of zone on unknown sled {sled_id}"
20912129 ) )
20922130 } ) ?;
2093- editor
2094- . set_host_phase_2_slot ( slot, host_phase_2)
2095- . map_err ( |err| Error :: SledEditError { sled_id, err } )
2131+ editor. set_host_phase_2_slot ( slot, host_phase_2) ;
2132+ Ok ( ( ) )
20962133 }
20972134
20982135 /// Set the `remove_mupdate_override` field of the given sled.
@@ -2106,9 +2143,8 @@ impl<'a> BlueprintBuilder<'a> {
21062143 "tried to set sled state for unknown sled {sled_id}"
21072144 ) )
21082145 } ) ?;
2109- editor
2110- . set_remove_mupdate_override ( remove_mupdate_override)
2111- . map_err ( |err| Error :: SledEditError { sled_id, err } )
2146+ editor. set_remove_mupdate_override ( remove_mupdate_override) ;
2147+ Ok ( ( ) )
21122148 }
21132149
21142150 fn sled_add_zone (
@@ -2136,6 +2172,7 @@ impl<'a> BlueprintBuilder<'a> {
21362172 } ) ?;
21372173 editor
21382174 . alloc_underlay_ip ( )
2175+ . ok_or ( SledEditError :: OutOfUnderlayIps )
21392176 . map_err ( |err| Error :: SledEditError { sled_id, err } )
21402177 }
21412178
@@ -2279,9 +2316,8 @@ impl<'a> BlueprintBuilder<'a> {
22792316 "tried to force generation bump for unknown sled {sled_id}"
22802317 ) )
22812318 } ) ?;
2282- editor
2283- . debug_force_generation_bump ( )
2284- . map_err ( |err| Error :: SledEditError { sled_id, err } )
2319+ editor. debug_force_generation_bump ( ) ;
2320+ Ok ( ( ) )
22852321 }
22862322}
22872323
0 commit comments