Skip to content

Commit d333576

Browse files
authored
incorporate SP updates into planner (#8269)
1 parent 3fec99e commit d333576

File tree

10 files changed

+152
-13
lines changed

10 files changed

+152
-13
lines changed

dev-tools/omdb/tests/usage_errors.out

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -508,19 +508,22 @@ Options:
508508
Show sleds that match the given filter
509509

510510
Possible values:
511-
- all: All sleds in the system, regardless of policy or state
512-
- commissioned: All sleds that are currently part of the control plane cluster
513-
- decommissioned: All sleds that were previously part of the control plane
511+
- all: All sleds in the system, regardless of policy or state
512+
- commissioned: All sleds that are currently part of the control plane
513+
cluster
514+
- decommissioned: All sleds that were previously part of the control plane
514515
cluster but have been decommissioned
515-
- discretionary: Sleds that are eligible for discretionary services
516-
- in-service: Sleds that are in service (even if they might not be eligible
517-
for discretionary services)
518-
- query-during-inventory: Sleds whose sled agents should be queried for inventory
519-
- reservation-create: Sleds on which reservations can be created
520-
- vpc-routing: Sleds which should be sent OPTE V2P mappings and Routing rules
521-
- vpc-firewall: Sleds which should be sent VPC firewall rules
522-
- tuf-artifact-replication: Sleds which should have TUF repo artifacts replicated onto
523-
them
516+
- discretionary: Sleds that are eligible for discretionary services
517+
- in-service: Sleds that are in service (even if they might not be
518+
eligible for discretionary services)
519+
- query-during-inventory: Sleds whose sled agents should be queried for inventory
520+
- reservation-create: Sleds on which reservations can be created
521+
- vpc-routing: Sleds which should be sent OPTE V2P mappings and Routing
522+
rules
523+
- vpc-firewall: Sleds which should be sent VPC firewall rules
524+
- tuf-artifact-replication: Sleds which should have TUF repo artifacts replicated
525+
onto them
526+
- sps-updated-by-reconfigurator: Sleds whose SPs should be updated by Reconfigurator
524527

525528
--log-level <LOG_LEVEL>
526529
log level filter

dev-tools/reconfigurator-cli/tests/output/cmds-example-stdout

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,7 @@ WARN failed to place all new desired InternalDns zones, placed: 0, wanted_to_pla
497497
INFO sufficient ExternalDns zones exist in plan, desired_count: 0, current_count: 0
498498
WARN failed to place all new desired Nexus zones, placed: 0, wanted_to_place: 3
499499
INFO sufficient Oximeter zones exist in plan, desired_count: 0, current_count: 0
500+
WARN cannot issue more SP updates (no current artifacts)
500501
INFO some zones not yet up-to-date, sled_id: 89d02b1b-478c-401a-8e28-7a26f74fa41b
501502
INFO will ensure cockroachdb setting, setting: cluster.preserve_downgrade_option, value: DoNotModify
502503
generated blueprint 86db3308-f817-4626-8838-4085949a6a41 based on parent blueprint ade5749d-bdf3-4fab-a8ae-00bea01b3a5a

dev-tools/reconfigurator-cli/tests/output/cmds-expunge-newly-added-external-dns-stdout

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,7 @@ INFO sufficient InternalDns zones exist in plan, desired_count: 3, current_count
974974
INFO added zone to sled, sled_id: a88790de-5962-4871-8686-61c1fd5b7094, kind: ExternalDns
975975
INFO sufficient Nexus zones exist in plan, desired_count: 3, current_count: 3
976976
INFO sufficient Oximeter zones exist in plan, desired_count: 0, current_count: 0
977+
WARN cannot issue more SP updates (no current artifacts)
977978
INFO some zones not yet up-to-date, sled_id: a88790de-5962-4871-8686-61c1fd5b7094
978979
INFO will ensure cockroachdb setting, setting: cluster.preserve_downgrade_option, value: DoNotModify
979980
generated blueprint 9c998c1d-1a7b-440a-ae0c-40f781dea6e2 based on parent blueprint 366b0b68-d80e-4bc1-abd3-dc69837847e0

dev-tools/reconfigurator-cli/tests/output/cmds-expunge-newly-added-internal-dns-stdout

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,7 @@ INFO added zone to sled, sled_id: d81c6a84-79b8-4958-ae41-ea46c9b19763, kind: In
10041004
INFO sufficient ExternalDns zones exist in plan, desired_count: 3, current_count: 3
10051005
INFO sufficient Nexus zones exist in plan, desired_count: 3, current_count: 3
10061006
INFO sufficient Oximeter zones exist in plan, desired_count: 0, current_count: 0
1007+
WARN cannot issue more SP updates (no current artifacts)
10071008
INFO some zones not yet up-to-date, sled_id: d81c6a84-79b8-4958-ae41-ea46c9b19763
10081009
INFO will ensure cockroachdb setting, setting: cluster.preserve_downgrade_option, value: DoNotModify
10091010
generated blueprint af934083-59b5-4bf6-8966-6fb5292c29e1 based on parent blueprint 58d5e830-0884-47d8-a7cd-b2b3751adeb4

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,7 @@ mod tests {
18841884
use nexus_types::external_api::views::PhysicalDiskState;
18851885
use nexus_types::external_api::views::SledPolicy;
18861886
use nexus_types::external_api::views::SledState;
1887+
use nexus_types::inventory::BaseboardId;
18871888
use nexus_types::inventory::Collection;
18881889
use omicron_common::address::IpRange;
18891890
use omicron_common::address::Ipv6Subnet;
@@ -2002,6 +2003,10 @@ mod tests {
20022003
policy: SledPolicy::provisionable(),
20032004
state: SledState::Active,
20042005
resources,
2006+
baseboard_id: BaseboardId {
2007+
part_number: String::from("unused"),
2008+
serial_number: String::from("unused"),
2009+
},
20052010
}
20062011
}
20072012

nexus/reconfigurator/planning/src/blueprint_builder/builder.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,13 @@ impl<'a> BlueprintBuilder<'a> {
19601960
Ok(self.resource_allocator()?.inject_untracked_external_dns_ip(addr)?)
19611961
}
19621962

1963+
pub fn pending_mgs_updates_replace_all(
1964+
&mut self,
1965+
updates: nexus_types::deployment::PendingMgsUpdates,
1966+
) {
1967+
self.pending_mgs_updates = updates;
1968+
}
1969+
19631970
pub fn pending_mgs_update_insert(
19641971
&mut self,
19651972
update: nexus_types::deployment::PendingMgsUpdate,

nexus/reconfigurator/planning/src/planner.rs

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use crate::blueprint_builder::Error;
1313
use crate::blueprint_builder::Operation;
1414
use crate::blueprint_editor::DisksEditError;
1515
use crate::blueprint_editor::SledEditError;
16+
use crate::mgs_updates::plan_mgs_updates;
1617
use crate::planner::omicron_zone_placement::PlacementError;
18+
use gateway_client::types::SpType;
1719
use nexus_sled_agent_shared::inventory::OmicronZoneType;
1820
use nexus_sled_agent_shared::inventory::ZoneKind;
1921
use nexus_types::deployment::Blueprint;
@@ -50,6 +52,37 @@ pub use self::rng::SledPlannerRng;
5052
mod omicron_zone_placement;
5153
pub(crate) mod rng;
5254

55+
/// Maximum number of MGS-managed updates (updates to SP, RoT, RoT bootloader,
56+
/// or host OS) that we allow to be pending across the whole system at one time
57+
///
58+
/// For now, we limit this to 1 for safety. That's for a few reasons:
59+
///
60+
/// - SP updates reboot the corresponding host. Thus, if we have one of these
61+
/// updates outstanding, we should assume that host may be offline. Most
62+
/// control plane services are designed to survive multiple failures (e.g.,
63+
/// the Cockroach cluster can sustain two failures and stay online), but
64+
/// having one sled offline eats into that margin. And some services like
65+
/// Crucible volumes can only sustain one failure. Taking down two sleds
66+
/// would render unavailable any Crucible volumes with regions on those two
67+
/// sleds.
68+
///
69+
/// - There is unfortunately some risk in updating the RoT bootloader, in that
70+
/// there's a window where a failure could render the device unbootable. See
71+
/// oxidecomputer/omicron#7819 for more on this. Updating only one at a time
72+
/// helps mitigate this risk.
73+
///
74+
/// More sophisticated schemes are certainly possible (e.g., allocate Crucible
75+
/// regions in such a way that there are at least pairs of sleds we could update
76+
/// concurrently without taking volumes down; and/or be willing to update
77+
/// multiple sleds as long as they don't have overlapping control plane
78+
/// services, etc.).
79+
const NUM_CONCURRENT_MGS_UPDATES: usize = 1;
80+
81+
enum UpdateStepResult {
82+
ContinueToNextStep,
83+
Waiting,
84+
}
85+
5386
pub struct Planner<'a> {
5487
log: Logger,
5588
input: &'a PlanningInput,
@@ -115,7 +148,10 @@ impl<'a> Planner<'a> {
115148
self.do_plan_expunge()?;
116149
self.do_plan_add()?;
117150
self.do_plan_decommission()?;
118-
self.do_plan_zone_updates()?;
151+
if let UpdateStepResult::ContinueToNextStep = self.do_plan_mgs_updates()
152+
{
153+
self.do_plan_zone_updates()?;
154+
}
119155
self.do_plan_cockroachdb_settings();
120156
Ok(())
121157
}
@@ -901,6 +937,63 @@ impl<'a> Planner<'a> {
901937
Ok(())
902938
}
903939

940+
/// Update at most one MGS-managed device (SP, RoT, etc.), if any are out of
941+
/// date.
942+
fn do_plan_mgs_updates(&mut self) -> UpdateStepResult {
943+
// Determine which baseboards we will consider updating.
944+
//
945+
// Sleds may be present but not adopted as part of the control plane.
946+
// In deployed systems, this would probably only happen if a sled was
947+
// about to be added. In dev/test environments, it's common to leave
948+
// some number of sleds out of the control plane for various reasons.
949+
// Inventory will still report them, but we don't want to touch them.
950+
//
951+
// For better or worse, switches and PSCs do not have the same idea of
952+
// being adopted into the control plane. If they're present, they're
953+
// part of the system, and we will update them.
954+
let included_sled_baseboards: BTreeSet<_> = self
955+
.input
956+
.all_sleds(SledFilter::SpsUpdatedByReconfigurator)
957+
.map(|(_sled_id, details)| &details.baseboard_id)
958+
.collect();
959+
let included_baseboards =
960+
self.inventory
961+
.sps
962+
.iter()
963+
.filter_map(|(baseboard_id, sp_state)| {
964+
let do_include = match sp_state.sp_type {
965+
SpType::Sled => included_sled_baseboards
966+
.contains(baseboard_id.as_ref()),
967+
SpType::Power => true,
968+
SpType::Switch => true,
969+
};
970+
do_include.then_some(baseboard_id.clone())
971+
})
972+
.collect();
973+
974+
// Compute the new set of PendingMgsUpdates.
975+
let current_updates =
976+
&self.blueprint.parent_blueprint().pending_mgs_updates;
977+
let current_artifacts = self.input.tuf_repo();
978+
let next = plan_mgs_updates(
979+
&self.log,
980+
&self.inventory,
981+
&included_baseboards,
982+
&current_updates,
983+
current_artifacts,
984+
NUM_CONCURRENT_MGS_UPDATES,
985+
);
986+
987+
// TODO This is not quite right. See oxidecomputer/omicron#8285.
988+
let rv = if next.is_empty() {
989+
UpdateStepResult::ContinueToNextStep
990+
} else {
991+
UpdateStepResult::Waiting
992+
};
993+
self.blueprint.pending_mgs_updates_replace_all(next);
994+
rv
995+
}
996+
904997
/// Update at most one existing zone to use a new image source.
905998
fn do_plan_zone_updates(&mut self) -> Result<(), Error> {
906999
// We are only interested in non-decommissioned sleds.

nexus/reconfigurator/planning/src/system.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,18 @@ impl SystemDescription {
491491
policy: sled.policy,
492492
state: sled.state,
493493
resources: sled.resources.clone(),
494+
baseboard_id: BaseboardId {
495+
part_number: sled
496+
.inventory_sled_agent
497+
.baseboard
498+
.model()
499+
.to_owned(),
500+
serial_number: sled
501+
.inventory_sled_agent
502+
.baseboard
503+
.identifier()
504+
.to_owned(),
505+
},
494506
};
495507
builder.add_sled(sled.sled_id, sled_details)?;
496508
}

nexus/reconfigurator/preparation/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use nexus_types::deployment::SledResources;
3333
use nexus_types::deployment::UnstableReconfiguratorState;
3434
use nexus_types::identity::Asset;
3535
use nexus_types::identity::Resource;
36+
use nexus_types::inventory::BaseboardId;
3637
use nexus_types::inventory::Collection;
3738
use omicron_common::address::IpRange;
3839
use omicron_common::address::Ipv6Subnet;
@@ -274,6 +275,10 @@ impl PlanningInputFromDb<'_> {
274275
policy: sled_row.policy(),
275276
state: sled_row.state().into(),
276277
resources: SledResources { subnet, zpools },
278+
baseboard_id: BaseboardId {
279+
part_number: sled_row.part_number().to_owned(),
280+
serial_number: sled_row.serial_number().to_owned(),
281+
},
277282
};
278283
// TODO-cleanup use `TypedUuid` everywhere
279284
let sled_id = SledUuid::from_untyped_uuid(sled_id);

nexus/types/src/deployment/planning_input.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::external_api::views::PhysicalDiskState;
1414
use crate::external_api::views::SledPolicy;
1515
use crate::external_api::views::SledProvisionPolicy;
1616
use crate::external_api::views::SledState;
17+
use crate::inventory::BaseboardId;
1718
use chrono::DateTime;
1819
use chrono::Utc;
1920
use clap::ValueEnum;
@@ -724,6 +725,9 @@ pub enum SledFilter {
724725

725726
/// Sleds which should have TUF repo artifacts replicated onto them.
726727
TufArtifactReplication,
728+
729+
/// Sleds whose SPs should be updated by Reconfigurator
730+
SpsUpdatedByReconfigurator,
727731
}
728732

729733
impl SledFilter {
@@ -780,6 +784,7 @@ impl SledPolicy {
780784
SledFilter::VpcRouting => true,
781785
SledFilter::VpcFirewall => true,
782786
SledFilter::TufArtifactReplication => true,
787+
SledFilter::SpsUpdatedByReconfigurator => true,
783788
},
784789
SledPolicy::InService {
785790
provision_policy: SledProvisionPolicy::NonProvisionable,
@@ -794,6 +799,7 @@ impl SledPolicy {
794799
SledFilter::VpcRouting => true,
795800
SledFilter::VpcFirewall => true,
796801
SledFilter::TufArtifactReplication => true,
802+
SledFilter::SpsUpdatedByReconfigurator => true,
797803
},
798804
SledPolicy::Expunged => match filter {
799805
SledFilter::All => true,
@@ -806,6 +812,7 @@ impl SledPolicy {
806812
SledFilter::VpcRouting => false,
807813
SledFilter::VpcFirewall => false,
808814
SledFilter::TufArtifactReplication => false,
815+
SledFilter::SpsUpdatedByReconfigurator => false,
809816
},
810817
}
811818
}
@@ -840,6 +847,7 @@ impl SledState {
840847
SledFilter::VpcRouting => true,
841848
SledFilter::VpcFirewall => true,
842849
SledFilter::TufArtifactReplication => true,
850+
SledFilter::SpsUpdatedByReconfigurator => true,
843851
},
844852
SledState::Decommissioned => match filter {
845853
SledFilter::All => true,
@@ -852,6 +860,7 @@ impl SledState {
852860
SledFilter::VpcRouting => false,
853861
SledFilter::VpcFirewall => false,
854862
SledFilter::TufArtifactReplication => false,
863+
SledFilter::SpsUpdatedByReconfigurator => false,
855864
},
856865
}
857866
}
@@ -1058,6 +1067,8 @@ pub struct SledDetails {
10581067
pub state: SledState,
10591068
/// current resources allocated to this sled
10601069
pub resources: SledResources,
1070+
/// baseboard id for this sled
1071+
pub baseboard_id: BaseboardId,
10611072
}
10621073

10631074
#[derive(Debug, thiserror::Error)]

0 commit comments

Comments
 (0)