diff --git a/Cargo.lock b/Cargo.lock index 84ec9b702db..efb21ff54ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6710,7 +6710,6 @@ dependencies = [ "nexus-config", "nexus-db-schema", "nexus-defaults", - "nexus-sled-agent-shared", "nexus-types", "omicron-certificates", "omicron-cockroach-metrics", @@ -6731,6 +6730,7 @@ dependencies = [ "serde", "serde_json", "sled-agent-client", + "sled-agent-types", "slog", "slog-error-chain", "steno", @@ -6785,7 +6785,6 @@ dependencies = [ "nexus-db-schema", "nexus-inventory", "nexus-reconfigurator-planning", - "nexus-sled-agent-shared", "nexus-test-utils", "nexus-types", "omicron-cockroach-metrics", @@ -6820,6 +6819,7 @@ dependencies = [ "serde_with", "sha2", "sled-agent-client", + "sled-agent-types", "slog", "slog-error-chain", "static_assertions", @@ -6922,7 +6922,6 @@ dependencies = [ "iddqd", "itertools 0.14.0", "nexus-client", - "nexus-sled-agent-shared", "nexus-types", "ntp-admin-client", "omicron-cockroach-metrics", @@ -6935,6 +6934,7 @@ dependencies = [ "serde_json", "sled-agent-client", "sled-agent-types", + "sled-agent-types-versions", "sled-agent-zone-images-examples", "slog", "slog-error-chain", @@ -6969,7 +6969,6 @@ dependencies = [ "chrono", "futures", "iddqd", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-passwords", @@ -6982,6 +6981,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", + "sled-agent-types-versions", "slog", "uuid", ] @@ -7043,7 +7043,6 @@ dependencies = [ "iddqd", "internal-dns-resolver", "internal-dns-types", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-test-utils", @@ -7093,12 +7092,12 @@ name = "nexus-reconfigurator-blippy" version = "0.1.0" dependencies = [ "nexus-reconfigurator-planning", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", + "sled-agent-types", "tufaceous-artifact", ] @@ -7164,7 +7163,6 @@ dependencies = [ "nexus-networking", "nexus-reconfigurator-planning", "nexus-reconfigurator-preparation", - "nexus-sled-agent-shared", "nexus-test-utils", "nexus-test-utils-macros", "nexus-types", @@ -7178,6 +7176,7 @@ dependencies = [ "pq-sys", "reqwest", "sled-agent-client", + "sled-agent-types", "slog", "slog-error-chain", "tokio", @@ -7218,7 +7217,6 @@ dependencies = [ "nexus-inventory", "nexus-reconfigurator-blippy", "nexus-reconfigurator-simulation", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-test-utils", @@ -7231,6 +7229,7 @@ dependencies = [ "reconfigurator-cli", "semver 1.0.27", "sled-agent-client", + "sled-agent-types", "sled-hardware-types", "slog", "slog-error-chain", @@ -7302,13 +7301,13 @@ dependencies = [ "itertools 0.14.0", "nexus-inventory", "nexus-reconfigurator-planning", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "petname", "sapling-renderdag", + "sled-agent-types", "slog", "strum 0.27.2", "swrite", @@ -7346,32 +7345,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "nexus-sled-agent-shared" -version = "0.1.0" -dependencies = [ - "camino", - "chrono", - "daft", - "iddqd", - "illumos-utils", - "indent_write", - "omicron-common", - "omicron-passwords", - "omicron-uuid-kinds", - "omicron-workspace-hack", - "proptest", - "schemars 0.8.22", - "serde", - "serde_json", - "sled-hardware-types", - "strum 0.27.2", - "test-strategy", - "thiserror 2.0.17", - "tufaceous-artifact", - "uuid", -] - [[package]] name = "nexus-test-interface" version = "0.1.0" @@ -7379,13 +7352,13 @@ dependencies = [ "async-trait", "nexus-config", "nexus-db-queries", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-rpaths", "omicron-uuid-kinds", "omicron-workspace-hack", "pq-sys", + "sled-agent-types", "slog", "tokio", "uuid", @@ -7421,7 +7394,6 @@ dependencies = [ "nexus-config", "nexus-db-queries", "nexus-lockstep-client", - "nexus-sled-agent-shared", "nexus-test-interface", "nexus-types", "omicron-cockroach-admin", @@ -7440,6 +7412,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sled-agent-client", + "sled-agent-types", "slog", "slog-error-chain", "tokio", @@ -7489,7 +7462,6 @@ dependencies = [ "itertools 0.14.0", "newtype-uuid", "newtype_derive", - "nexus-sled-agent-shared", "omicron-common", "omicron-passwords", "omicron-test-utils", @@ -7507,6 +7479,7 @@ dependencies = [ "serde", "serde_json", "serde_with", + "sled-agent-types-versions", "sled-hardware-types", "slog", "slog-error-chain", @@ -8210,7 +8183,6 @@ dependencies = [ "nexus-lockstep-client", "nexus-reconfigurator-planning", "nexus-reconfigurator-preparation", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-rpaths", @@ -8220,6 +8192,7 @@ dependencies = [ "pq-sys", "reqwest", "serde", + "sled-agent-types", "slog", "slog-error-chain", "textwrap 0.16.2", @@ -8332,7 +8305,6 @@ dependencies = [ "nexus-reconfigurator-preparation", "nexus-reconfigurator-rendezvous", "nexus-saga-recovery", - "nexus-sled-agent-shared", "nexus-test-interface", "nexus-test-utils", "nexus-test-utils-macros", @@ -8507,7 +8479,6 @@ dependencies = [ "nexus-lockstep-client", "nexus-reconfigurator-preparation", "nexus-saga-recovery", - "nexus-sled-agent-shared", "nexus-test-utils", "nexus-test-utils-macros", "nexus-types", @@ -8531,6 +8502,7 @@ dependencies = [ "serde_json", "sigpipe", "sled-agent-client", + "sled-agent-types", "slog", "slog-error-chain", "steno", @@ -8785,7 +8757,6 @@ dependencies = [ "nexus-config", "nexus-lockstep-client", "nexus-reconfigurator-blippy", - "nexus-sled-agent-shared", "nexus-types", "ntp-admin-client", "omicron-common", @@ -8822,6 +8793,7 @@ dependencies = [ "sled-agent-client", "sled-agent-config-reconciler", "sled-agent-types", + "sled-agent-types-versions", "sled-agent-zone-images", "sled-diagnostics", "sled-hardware", @@ -11359,7 +11331,6 @@ dependencies = [ "nexus-reconfigurator-blippy", "nexus-reconfigurator-planning", "nexus-reconfigurator-simulation", - "nexus-sled-agent-shared", "nexus-types", "omicron-common", "omicron-repl-utils", @@ -11371,6 +11342,7 @@ dependencies = [ "semver 1.0.27", "serde", "serde_json", + "sled-agent-types", "slog", "slog-error-chain", "slog-term", @@ -12854,7 +12826,6 @@ dependencies = [ "dropshot-api-manager-types", "http", "iddqd", - "nexus-sled-agent-shared", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -12862,6 +12833,7 @@ dependencies = [ "semver 1.0.27", "serde", "sled-agent-types", + "sled-agent-types-versions", "sled-diagnostics", "sled-hardware-types", "tufaceous-artifact", @@ -12875,7 +12847,6 @@ dependencies = [ "anyhow", "async-trait", "chrono", - "nexus-sled-agent-shared", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -12888,6 +12859,7 @@ dependencies = [ "serde", "serde_json", "sled-agent-types", + "sled-agent-types-versions", "slog", "uuid", ] @@ -12914,7 +12886,6 @@ dependencies = [ "illumos-utils", "installinator-common", "key-manager", - "nexus-sled-agent-shared", "ntp-admin-client", "omicron-common", "omicron-test-utils", @@ -12929,6 +12900,7 @@ dependencies = [ "sha2", "sled-agent-api", "sled-agent-types", + "sled-agent-types-versions", "sled-agent-zone-images-examples", "sled-hardware", "sled-storage", @@ -12954,7 +12926,6 @@ dependencies = [ "chrono", "daft", "iddqd", - "nexus-sled-agent-shared", "omicron-common", "omicron-test-utils", "omicron-uuid-kinds", @@ -12967,6 +12938,7 @@ dependencies = [ "serde_human_bytes", "serde_json", "sha3", + "sled-agent-types-versions", "sled-hardware-types", "slog", "slog-error-chain", @@ -12978,6 +12950,39 @@ dependencies = [ "uuid", ] +[[package]] +name = "sled-agent-types-versions" +version = "0.1.0" +dependencies = [ + "async-trait", + "bootstore", + "camino", + "chrono", + "daft", + "iddqd", + "illumos-utils", + "indent_write", + "omicron-common", + "omicron-passwords", + "omicron-test-utils", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "oxnet", + "propolis_api_types", + "proptest", + "schemars 0.8.22", + "serde", + "serde_json", + "sha3", + "sled-hardware-types", + "slog", + "strum 0.27.2", + "test-strategy", + "thiserror 2.0.17", + "tufaceous-artifact", + "uuid", +] + [[package]] name = "sled-agent-zone-images" version = "0.1.0" @@ -12988,7 +12993,6 @@ dependencies = [ "expectorate", "iddqd", "illumos-utils", - "nexus-sled-agent-shared", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -13014,7 +13018,6 @@ dependencies = [ "camino", "camino-tempfile-ext", "iddqd", - "nexus-sled-agent-shared", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", diff --git a/Cargo.toml b/Cargo.toml index e1632dec958..0b72863b281 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,7 +73,6 @@ members = [ "live-tests/macros", "nexus", "nexus-config", - "nexus-sled-agent-shared", "nexus/authz-macros", "nexus/auth", "nexus/background-task-interface", @@ -131,6 +130,7 @@ members = [ "sled-agent/config-reconciler", "sled-agent/repo-depot-api", "sled-agent/types", + "sled-agent/types/versions", "sled-agent/zone-images", "sled-agent/zone-images-examples", "sled-diagnostics", @@ -235,7 +235,6 @@ default-members = [ "live-tests/macros", "nexus", "nexus-config", - "nexus-sled-agent-shared", "nexus/authz-macros", "nexus/auth", "nexus/background-task-interface", @@ -293,6 +292,7 @@ default-members = [ "sled-agent/config-reconciler", "sled-agent/repo-depot-api", "sled-agent/types", + "sled-agent/types/versions", "sled-agent/zone-images", "sled-agent/zone-images-examples", "sled-diagnostics", @@ -568,7 +568,6 @@ nexus-reconfigurator-preparation = { path = "nexus/reconfigurator/preparation" } nexus-reconfigurator-rendezvous = { path = "nexus/reconfigurator/rendezvous" } nexus-reconfigurator-simulation = { path = "nexus/reconfigurator/simulation" } nexus-saga-recovery = { path = "nexus/saga-recovery" } -nexus-sled-agent-shared = { path = "nexus-sled-agent-shared" } nexus-test-interface = { path = "nexus/test-interface" } nexus-test-utils-macros = { path = "nexus/test-utils-macros" } nexus-test-utils = { path = "nexus/test-utils" } @@ -712,6 +711,7 @@ sled-agent-api = { path = "sled-agent/api" } sled-agent-client = { path = "clients/sled-agent-client" } sled-agent-config-reconciler = { path = "sled-agent/config-reconciler" } sled-agent-types = { path = "sled-agent/types" } +sled-agent-types-versions = { path = "sled-agent/types/versions" } sled-agent-zone-images = { path = "sled-agent/zone-images" } sled-agent-zone-images-examples = { path = "sled-agent/zone-images-examples" } sled-diagnostics = { path = "sled-diagnostics" } @@ -971,8 +971,9 @@ opt-level = 3 # It's common during development to use a local copy of various complex # dependencies. If you want to use those, uncomment one of these blocks. # -# [patch.crates-io] +[patch.crates-io] # diesel = { path = "../../diesel/diesel" } +# drift = { path = "../drift" } # dropshot = { path = "../dropshot/dropshot" } # dropshot_endpoint = { path = "../dropshot/dropshot_endpoint" } # dropshot-api-manager = { path = "../dropshot-api-manager/crates/dropshot-api-manager" } diff --git a/clients/nexus-lockstep-client/Cargo.toml b/clients/nexus-lockstep-client/Cargo.toml index c5bb3d47be0..ccc1d05940d 100644 --- a/clients/nexus-lockstep-client/Cargo.toml +++ b/clients/nexus-lockstep-client/Cargo.toml @@ -11,7 +11,7 @@ workspace = true chrono.workspace = true futures.workspace = true iddqd.workspace = true -nexus-sled-agent-shared.workspace = true +sled-agent-types-versions.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true diff --git a/clients/nexus-lockstep-client/src/lib.rs b/clients/nexus-lockstep-client/src/lib.rs index 16748df18fc..d6a3b52a7f0 100644 --- a/clients/nexus-lockstep-client/src/lib.rs +++ b/clients/nexus-lockstep-client/src/lib.rs @@ -64,7 +64,7 @@ progenitor::generate_api!( ReconfiguratorConfig = nexus_types::deployment::ReconfiguratorConfig, ReconfiguratorConfigParam = nexus_types::deployment::ReconfiguratorConfigParam, ReconfiguratorConfigView = nexus_types::deployment::ReconfiguratorConfigView, - RecoverySiloConfig = nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig, + RecoverySiloConfig = sled_agent_types_versions::latest::rack_init::RecoverySiloConfig, SledAgentUpdateStatus = nexus_types::internal_api::views::SledAgentUpdateStatus, UpdateStatus = nexus_types::internal_api::views::UpdateStatus, ZoneStatus = nexus_types::internal_api::views::ZoneStatus, diff --git a/clients/sled-agent-client/Cargo.toml b/clients/sled-agent-client/Cargo.toml index 421b49243d6..94de5963077 100644 --- a/clients/sled-agent-client/Cargo.toml +++ b/clients/sled-agent-client/Cargo.toml @@ -11,7 +11,7 @@ workspace = true anyhow.workspace = true async-trait.workspace = true chrono.workspace = true -nexus-sled-agent-shared.workspace = true +sled-agent-types-versions.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/clients/sled-agent-client/src/lib.rs b/clients/sled-agent-client/src/lib.rs index 3d06fd47b54..59204f21452 100644 --- a/clients/sled-agent-client/src/lib.rs +++ b/clients/sled-agent-client/src/lib.rs @@ -46,7 +46,7 @@ progenitor::generate_api!( "oxnet" = "0.1.0", }, replace = { - Baseboard = nexus_sled_agent_shared::inventory::Baseboard, + Baseboard = sled_agent_types_versions::latest::inventory::Baseboard, ByteCount = omicron_common::api::external::ByteCount, DatasetsConfig = omicron_common::disk::DatasetsConfig, DatasetManagementStatus = omicron_common::disk::DatasetManagementStatus, @@ -62,21 +62,21 @@ progenitor::generate_api!( Generation = omicron_common::api::external::Generation, Hostname = omicron_common::api::external::Hostname, ImportExportPolicy = omicron_common::api::external::ImportExportPolicy, - Inventory = nexus_sled_agent_shared::inventory::Inventory, - InventoryDisk = nexus_sled_agent_shared::inventory::InventoryDisk, - InventoryZpool = nexus_sled_agent_shared::inventory::InventoryZpool, + Inventory = sled_agent_types_versions::latest::inventory::Inventory, + InventoryDisk = sled_agent_types_versions::latest::inventory::InventoryDisk, + InventoryZpool = sled_agent_types_versions::latest::inventory::InventoryZpool, MacAddr = omicron_common::api::external::MacAddr, - MupdateOverrideBootInventory = nexus_sled_agent_shared::inventory::MupdateOverrideBootInventory, + MupdateOverrideBootInventory = sled_agent_types_versions::latest::inventory::MupdateOverrideBootInventory, Name = omicron_common::api::external::Name, NetworkInterface = omicron_common::api::internal::shared::NetworkInterface, OmicronPhysicalDiskConfig = omicron_common::disk::OmicronPhysicalDiskConfig, OmicronPhysicalDisksConfig = omicron_common::disk::OmicronPhysicalDisksConfig, - OmicronSledConfig = nexus_sled_agent_shared::inventory::OmicronSledConfig, - OmicronZoneConfig = nexus_sled_agent_shared::inventory::OmicronZoneConfig, - OmicronZoneDataset = nexus_sled_agent_shared::inventory::OmicronZoneDataset, - OmicronZoneImageSource = nexus_sled_agent_shared::inventory::OmicronZoneImageSource, - OmicronZoneType = nexus_sled_agent_shared::inventory::OmicronZoneType, - OmicronZonesConfig = nexus_sled_agent_shared::inventory::OmicronZonesConfig, + OmicronSledConfig = sled_agent_types_versions::latest::inventory::OmicronSledConfig, + OmicronZoneConfig = sled_agent_types_versions::latest::inventory::OmicronZoneConfig, + OmicronZoneDataset = sled_agent_types_versions::latest::inventory::OmicronZoneDataset, + OmicronZoneImageSource = sled_agent_types_versions::latest::inventory::OmicronZoneImageSource, + OmicronZoneType = sled_agent_types_versions::latest::inventory::OmicronZoneType, + OmicronZonesConfig = sled_agent_types_versions::latest::inventory::OmicronZonesConfig, PortFec = omicron_common::api::internal::shared::PortFec, PortSpeed = omicron_common::api::internal::shared::PortSpeed, RouterId = omicron_common::api::internal::shared::RouterId, @@ -85,7 +85,7 @@ progenitor::generate_api!( ResolvedVpcRouteSet = omicron_common::api::internal::shared::ResolvedVpcRouteSet, RouterTarget = omicron_common::api::internal::shared::RouterTarget, RouterVersion = omicron_common::api::internal::shared::RouterVersion, - SledRole = nexus_sled_agent_shared::inventory::SledRole, + SledRole = sled_agent_types_versions::latest::inventory::SledRole, SourceNatConfigGeneric = omicron_common::api::internal::shared::SourceNatConfigGeneric, SwitchLocation = omicron_common::api::external::SwitchLocation, Vni = omicron_common::api::external::Vni, diff --git a/dev-tools/omdb/Cargo.toml b/dev-tools/omdb/Cargo.toml index ce376d4b67c..0e985a5cfb5 100644 --- a/dev-tools/omdb/Cargo.toml +++ b/dev-tools/omdb/Cargo.toml @@ -53,7 +53,6 @@ nexus-inventory.workspace = true nexus-lockstep-client.workspace = true nexus-reconfigurator-preparation.workspace = true nexus-saga-recovery.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true @@ -73,6 +72,7 @@ serde.workspace = true sigpipe.workspace = true serde_json.workspace = true sled-agent-client.workspace = true +sled-agent-types.workspace = true slog.workspace = true slog-error-chain.workspace = true steno.workspace = true diff --git a/dev-tools/reconfigurator-cli/Cargo.toml b/dev-tools/reconfigurator-cli/Cargo.toml index 927b721cbb5..5c19b513645 100644 --- a/dev-tools/reconfigurator-cli/Cargo.toml +++ b/dev-tools/reconfigurator-cli/Cargo.toml @@ -29,8 +29,8 @@ nexus-inventory.workspace = true nexus-reconfigurator-blippy.workspace = true nexus-reconfigurator-planning.workspace = true nexus-reconfigurator-simulation.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true +sled-agent-types.workspace = true omicron-common.workspace = true omicron-repl-utils.workspace = true omicron-uuid-kinds.workspace = true diff --git a/dev-tools/reconfigurator-cli/src/lib.rs b/dev-tools/reconfigurator-cli/src/lib.rs index 27f96d0b1a8..5f69431574a 100644 --- a/dev-tools/reconfigurator-cli/src/lib.rs +++ b/dev-tools/reconfigurator-cli/src/lib.rs @@ -34,7 +34,6 @@ use nexus_reconfigurator_simulation::{ }; use nexus_reconfigurator_simulation::{SimStateBuilder, SimTufRepoSource}; use nexus_reconfigurator_simulation::{SimTufRepoDescription, Simulator}; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::execution; use nexus_types::deployment::execution::blueprint_external_dns_config; use nexus_types::deployment::execution::blueprint_internal_dns_config; @@ -68,6 +67,7 @@ use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::VnicUuid; use omicron_uuid_kinds::{BlueprintUuid, MupdateOverrideUuid}; use omicron_uuid_kinds::{CollectionUuid, MupdateUuid}; +use sled_agent_types::inventory::ZoneKind; use slog_error_chain::InlineErrorChain; use std::borrow::Cow; use std::collections::BTreeSet; diff --git a/live-tests/Cargo.toml b/live-tests/Cargo.toml index 3db7886b66e..dca2b884fd2 100644 --- a/live-tests/Cargo.toml +++ b/live-tests/Cargo.toml @@ -28,8 +28,8 @@ nexus-inventory.workspace = true nexus-lockstep-client.workspace = true nexus-reconfigurator-planning.workspace = true nexus-reconfigurator-preparation.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true +sled-agent-types.workspace = true omicron-common.workspace = true omicron-test-utils.workspace = true omicron-uuid-kinds.workspace = true diff --git a/live-tests/tests/test_nexus_add_remove.rs b/live-tests/tests/test_nexus_add_remove.rs index 05e65f19b10..c8852caa07a 100644 --- a/live-tests/tests/test_nexus_add_remove.rs +++ b/live-tests/tests/test_nexus_add_remove.rs @@ -19,7 +19,6 @@ use nexus_reconfigurator_planning::blueprint_editor::ExternalNetworkingAllocator use nexus_reconfigurator_planning::planner::Planner; use nexus_reconfigurator_planning::planner::PlannerRng; use nexus_reconfigurator_preparation::PlanningInputFromDb; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::BlueprintZoneType; use nexus_types::deployment::PlannerConfig; @@ -28,6 +27,7 @@ use nexus_types::deployment::blueprint_zone_type; use omicron_common::address::NEXUS_LOCKSTEP_PORT; use omicron_test_utils::dev::poll::CondCheckError; use omicron_test_utils::dev::poll::wait_for_condition; +use sled_agent_types::inventory::ZoneKind; use slog::{debug, info}; use std::net::SocketAddrV6; use std::sync::Arc; diff --git a/nexus-sled-agent-shared/README.md b/nexus-sled-agent-shared/README.md deleted file mode 100644 index 77b4d644863..00000000000 --- a/nexus-sled-agent-shared/README.md +++ /dev/null @@ -1,67 +0,0 @@ -# nexus-sled-agent-shared - -Internal types shared between Nexus and sled-agent, with extra dependencies not -in omicron-common. - -## Guidelines - -This crate should only be used for **internal types and data structures.** - -It should only be used for types that are used by **both `sled-agent-types` and `nexus-types`**. Prefer to put types in `sled-agent-types` or `nexus-types` if possible. - -- If a type is used by `sled-agent-api`, as well as any part of Nexus except `nexus-types`, put it in `sled-agent-types`. -- If a type is used by `nexus-internal-api`, as well as any part of sled-agent except `sled-agent-types`, put it in `nexus-types`. -- Only if a type is used by both `sled-agent-types` and `nexus-types` should it go here. - -## Why not omicron-common? - -omicron-common is used by several workspaces, so adding a dependency to it has -a large impact. This crate was motivated by the need to be a place to put types -that have dependencies not already in omicron-common. - -For example, with this crate, `omicron-common` avoids a dependency on -omicron-passwords, which pulls in `argon2`. - -An alternative would be to add a non-default feature to omicron-common to -include these types. But that would result in many extra, unnecessary rebuilds --- both omicron-common and many of its dependents would need to be built twice, -once with the feature and once without. A separate crate avoids that. - -## Why not nexus-config? - -`nexus-config` is a similar crate that was split out of `omicron-common` for -dependency reasons. However, `nexus-config` depends on the rather heavyweight -tokio-postgres, a dependency that is not a necessary component of sled-agent. - -## Why not sled-agent-types or nexus-types? - -Types that are primarily used by sled-agent or nexus should continue to go in -those crates. However, types used by both `nexus-types` and `sled-agent-types` -should go here. `sled-agent-types` and `nexus-types` can thus avoid a -dependency on each other: they're both "on the same level" and neither -dependency direction is clearly correct. - -## Why not Progenitor-generated types? - -Many of our types are actually generated by Progenitor within the -`sled-agent-client` and `nexus-client` crates. However, at least some of them -inconvenient to deal with, such as `OmicronZonesConfig` -- particularly the -fact that it stored IP addresses as strings and not as structured data. Before -making this change, there were a number of spots that had to deal with the -generated type and had to account for a string being invalid. - -Now, though, those types are replaced with the copy in this crate. - -Another issue this crate solves is circular dependencies. Thinking about the -organization in terms of layers, there's the `-types` layer and the `-client` -layer. Now, `sled-agent-client` uses `replace` to substitute the types in -`sled-agent-types` with its own. So logically, the `-client` layer is expected -to depend on the `-types` layer. But sled-agent makes API calls to Nexus, so -previously `sled-agent-types` depended on `nexus-client`, and similarly, -`nexus-types` depended on `sled-agent-client`. If this crate didn't exist, -then there would be a circular dependency: - -`sled-agent-client` -> `sled-agent-types` -> `nexus-client` -> `nexus-types` -> `sled-agent-client` - -This crate breaks that cycle by providing a place for shared types, and no -longer making either `-types` depend on either `-client`. diff --git a/nexus-sled-agent-shared/src/lib.rs b/nexus-sled-agent-shared/src/lib.rs deleted file mode 100644 index 12fc040bbb7..00000000000 --- a/nexus-sled-agent-shared/src/lib.rs +++ /dev/null @@ -1,19 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! Internal types shared between Nexus and sled-agent, with extra dependencies -//! not in omicron-common. -//! -//! Only types that are shared between `nexus-types` and `sled-agent-types` -//! should go here. -//! -//! - If a type is used by `sled-agent-api` and Nexus, but is not required by -//! `nexus-types`, it should go in `sled-agent-types` instead. -//! - If a type is used by `nexus-internal-api` and Nexus, but is not required -//! by `sled-agent-types`, it should go in `nexus-types` instead. -//! -//! For more information, see the crate [README](../README.md). - -pub mod inventory; -pub mod recovery_silo; diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index 1145813c01d..ace4fa6e42b 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -129,7 +129,6 @@ nexus-reconfigurator-execution.workspace = true nexus-reconfigurator-planning.workspace = true nexus-reconfigurator-preparation.workspace = true nexus-reconfigurator-rendezvous.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true diff --git a/nexus/db-model/Cargo.toml b/nexus/db-model/Cargo.toml index ffce1c15e9a..58292ceffca 100644 --- a/nexus/db-model/Cargo.toml +++ b/nexus/db-model/Cargo.toml @@ -51,10 +51,10 @@ omicron-common.workspace = true nexus-config.workspace = true nexus-db-schema.workspace = true nexus-defaults.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true omicron-passwords.workspace = true sled-agent-client.workspace = true +sled-agent-types.workspace = true omicron-workspace-hack.workspace = true tufaceous-artifact.workspace = true diff --git a/nexus/db-model/src/deployment.rs b/nexus/db-model/src/deployment.rs index 68a44f8caa9..7e8d1b84346 100644 --- a/nexus/db-model/src/deployment.rs +++ b/nexus/db-model/src/deployment.rs @@ -27,7 +27,6 @@ use nexus_db_schema::schema::{ bp_pending_mgs_update_sp, bp_sled_metadata, bp_target, debug_log_blueprint_planning, }; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_types::deployment::BlueprintPhysicalDiskDisposition; use nexus_types::deployment::BlueprintTarget; use nexus_types::deployment::BlueprintZoneConfig; @@ -68,6 +67,7 @@ use omicron_uuid_kinds::{ GenericUuid, MupdateOverrideKind, OmicronZoneKind, OmicronZoneUuid, PhysicalDiskKind, SledKind, SledUuid, ZpoolKind, ZpoolUuid, }; +use sled_agent_types::inventory::OmicronZoneDataset; use std::net::{IpAddr, SocketAddrV6}; use std::sync::Arc; use uuid::Uuid; diff --git a/nexus/db-model/src/external_ip.rs b/nexus/db-model/src/external_ip.rs index 4588ddfd465..00a91029e69 100644 --- a/nexus/db-model/src/external_ip.rs +++ b/nexus/db-model/src/external_ip.rs @@ -17,7 +17,6 @@ use diesel::Selectable; use ipnetwork::IpNetwork; use nexus_db_schema::schema::external_ip; use nexus_db_schema::schema::floating_ip; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::OmicronZoneExternalFloatingIp; use nexus_types::deployment::OmicronZoneExternalIp; use nexus_types::deployment::OmicronZoneExternalSnatIp; @@ -37,6 +36,7 @@ use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use sled_agent_client::types::InstanceExternalIpBody; +use sled_agent_types::inventory::ZoneKind; use slog_error_chain::SlogInlineError; use std::convert::TryFrom; use std::net::IpAddr; diff --git a/nexus/db-model/src/inventory.rs b/nexus/db-model/src/inventory.rs index ce6bea7e863..19d62285dd7 100644 --- a/nexus/db-model/src/inventory.rs +++ b/nexus/db-model/src/inventory.rs @@ -43,26 +43,6 @@ use nexus_db_schema::schema::{ inv_service_processor, inv_sled_agent, inv_sled_boot_partition, inv_sled_config_reconciler, inv_zpool, sw_caboose, sw_root_of_trust_page, }; -use nexus_sled_agent_shared::inventory::BootImageHeader; -use nexus_sled_agent_shared::inventory::BootPartitionDetails; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredContents; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::MupdateOverrideBootInventory; -use nexus_sled_agent_shared::inventory::MupdateOverrideInventory; -use nexus_sled_agent_shared::inventory::MupdateOverrideNonBootInventory; -use nexus_sled_agent_shared::inventory::OrphanedDataset; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideBootSuccessInventory; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideInventory; -use nexus_sled_agent_shared::inventory::ZoneArtifactInventory; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; -use nexus_sled_agent_shared::inventory::ZoneManifestBootInventory; -use nexus_sled_agent_shared::inventory::ZoneManifestInventory; -use nexus_sled_agent_shared::inventory::ZoneManifestNonBootInventory; -use nexus_sled_agent_shared::inventory::{ - ConfigReconcilerInventoryResult, OmicronSledConfig, OmicronZoneConfig, - OmicronZoneDataset, OmicronZoneImageSource, OmicronZoneType, -}; use nexus_types::inventory::HostPhase1ActiveSlot; use nexus_types::inventory::{ BaseboardId, Caboose, CockroachStatus, Collection, @@ -92,6 +72,26 @@ use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolKind; use omicron_uuid_kinds::{CollectionKind, OmicronZoneKind}; use omicron_uuid_kinds::{CollectionUuid, OmicronZoneUuid}; +use sled_agent_types::inventory::BootImageHeader; +use sled_agent_types::inventory::BootPartitionDetails; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::HostPhase2DesiredContents; +use sled_agent_types::inventory::HostPhase2DesiredSlots; +use sled_agent_types::inventory::MupdateOverrideBootInventory; +use sled_agent_types::inventory::MupdateOverrideInventory; +use sled_agent_types::inventory::MupdateOverrideNonBootInventory; +use sled_agent_types::inventory::OrphanedDataset; +use sled_agent_types::inventory::RemoveMupdateOverrideBootSuccessInventory; +use sled_agent_types::inventory::RemoveMupdateOverrideInventory; +use sled_agent_types::inventory::ZoneArtifactInventory; +use sled_agent_types::inventory::ZoneImageResolverInventory; +use sled_agent_types::inventory::ZoneManifestBootInventory; +use sled_agent_types::inventory::ZoneManifestInventory; +use sled_agent_types::inventory::ZoneManifestNonBootInventory; +use sled_agent_types::inventory::{ + ConfigReconcilerInventoryResult, OmicronSledConfig, OmicronZoneConfig, + OmicronZoneDataset, OmicronZoneImageSource, OmicronZoneType, +}; use std::collections::BTreeSet; use std::net::{IpAddr, SocketAddrV6}; use std::time::Duration; @@ -869,27 +869,23 @@ impl_enum_type!( Scrimlet => b"scrimlet" ); -impl From for SledRole { - fn from(value: nexus_sled_agent_shared::inventory::SledRole) -> Self { +impl From for SledRole { + fn from(value: sled_agent_types::inventory::SledRole) -> Self { match value { - nexus_sled_agent_shared::inventory::SledRole::Gimlet => { - SledRole::Gimlet - } - nexus_sled_agent_shared::inventory::SledRole::Scrimlet => { + sled_agent_types::inventory::SledRole::Gimlet => SledRole::Gimlet, + sled_agent_types::inventory::SledRole::Scrimlet => { SledRole::Scrimlet } } } } -impl From for nexus_sled_agent_shared::inventory::SledRole { +impl From for sled_agent_types::inventory::SledRole { fn from(value: SledRole) -> Self { match value { - SledRole::Gimlet => { - nexus_sled_agent_shared::inventory::SledRole::Gimlet - } + SledRole::Gimlet => sled_agent_types::inventory::SledRole::Gimlet, SledRole::Scrimlet => { - nexus_sled_agent_shared::inventory::SledRole::Scrimlet + sled_agent_types::inventory::SledRole::Scrimlet } } } @@ -921,7 +917,7 @@ pub struct InvSledAgent { pub zone_image_resolver: InvZoneImageResolver, } -/// See [`nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus`]. +/// See [`sled_agent_types::inventory::ConfigReconcilerInventoryStatus`]. #[derive(Queryable, Clone, Debug, Selectable, Insertable)] #[diesel(table_name = inv_sled_agent)] pub struct InvConfigReconcilerStatus { @@ -1001,7 +997,7 @@ impl InvConfigReconcilerStatus { } } -// See [`nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus`]. +// See [`sled_agent_types::inventory::ConfigReconcilerInventoryStatus`]. impl_enum_type!( InvConfigReconcilerStatusKindEnum: @@ -1014,7 +1010,7 @@ impl_enum_type!( Idle => b"idle" ); -/// See [`nexus_sled_agent_shared::inventory::ConfigReconcilerInventory`]. +/// See [`sled_agent_types::inventory::ConfigReconcilerInventory`]. #[derive(Queryable, Clone, Debug, Selectable, Insertable)] #[diesel(table_name = inv_sled_config_reconciler)] pub struct InvSledConfigReconciler { @@ -1095,7 +1091,7 @@ impl InvSledConfigReconciler { } } -// See [`nexus_sled_agent_shared::inventory::DbRemoveMupdateOverrideBootSuccess`]. +// See [`sled_agent_types::inventory::DbRemoveMupdateOverrideBootSuccess`]. impl_enum_type!( RemoveMupdateOverrideBootSuccessEnum: @@ -1131,7 +1127,7 @@ impl From } } -/// See [`nexus_sled_agent_shared::inventory::RemoveMupdateOverrideInventory`]. +/// See [`sled_agent_types::inventory::RemoveMupdateOverrideInventory`]. #[derive(Queryable, Clone, Debug, Selectable, Insertable)] #[diesel(table_name = inv_sled_config_reconciler)] pub struct InvRemoveMupdateOverride { @@ -1195,7 +1191,7 @@ impl InvRemoveMupdateOverride { } } -/// See [`nexus_sled_agent_shared::inventory::BootPartitionDetails`]. +/// See [`sled_agent_types::inventory::BootPartitionDetails`]. #[derive(Queryable, Clone, Debug, Selectable, Insertable)] #[diesel(table_name = inv_sled_boot_partition)] pub struct InvSledBootPartition { @@ -2222,9 +2218,9 @@ impl From for ServiceKind { } } -impl From for nexus_sled_agent_shared::inventory::ZoneKind { +impl From for sled_agent_types::inventory::ZoneKind { fn from(zone_type: ZoneType) -> Self { - use nexus_sled_agent_shared::inventory::ZoneKind::*; + use sled_agent_types::inventory::ZoneKind::*; match zone_type { ZoneType::BoundaryNtp => BoundaryNtp, @@ -2243,9 +2239,9 @@ impl From for nexus_sled_agent_shared::inventory::ZoneKind { } } -impl From for ZoneType { - fn from(zone_kind: nexus_sled_agent_shared::inventory::ZoneKind) -> Self { - use nexus_sled_agent_shared::inventory::ZoneKind::*; +impl From for ZoneType { + fn from(zone_kind: sled_agent_types::inventory::ZoneKind) -> Self { + use sled_agent_types::inventory::ZoneKind::*; match zone_kind { BoundaryNtp => ZoneType::BoundaryNtp, @@ -2383,7 +2379,7 @@ impl_enum_type!( Artifact => b"artifact" ); -/// See [`nexus_sled_agent_shared::inventory::OmicronZoneConfig`]. +/// See [`sled_agent_types::inventory::OmicronZoneConfig`]. #[derive(Queryable, Clone, Debug, Selectable, Insertable)] #[diesel(table_name = inv_omicron_sled_config_zone)] pub struct InvOmicronSledConfigZone { diff --git a/nexus/db-model/src/network_interface.rs b/nexus/db-model/src/network_interface.rs index 19830de929b..68eb8ec5ca7 100644 --- a/nexus/db-model/src/network_interface.rs +++ b/nexus/db-model/src/network_interface.rs @@ -16,7 +16,6 @@ use ipnetwork::IpNetwork; use nexus_db_schema::schema::instance_network_interface; use nexus_db_schema::schema::network_interface; use nexus_db_schema::schema::service_network_interface; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::external_api::params; use nexus_types::identity::Resource; use omicron_common::api::external::Error; @@ -31,6 +30,7 @@ use omicron_uuid_kinds::VnicUuid; use oxnet::IpNet; use oxnet::Ipv4Net; use oxnet::Ipv6Net; +use sled_agent_types::inventory::ZoneKind; use std::net::IpAddr; use uuid::Uuid; diff --git a/nexus/db-model/src/omicron_zone_config.rs b/nexus/db-model/src/omicron_zone_config.rs index 9cbed0fbd4a..2eb60a156d8 100644 --- a/nexus/db-model/src/omicron_zone_config.rs +++ b/nexus/db-model/src/omicron_zone_config.rs @@ -15,13 +15,13 @@ use crate::{MacAddr, Name, SqlU8, SqlU16, SqlU32}; use anyhow::{Context, anyhow, bail, ensure}; use ipnetwork::IpNetwork; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_types::inventory::NetworkInterface; use omicron_common::api::internal::shared::{ NetworkInterfaceKind, PrivateIpConfig, }; use omicron_uuid_kinds::{GenericUuid, OmicronZoneUuid}; use oxnet::{Ipv4Net, Ipv6Net}; +use sled_agent_types::inventory::OmicronZoneDataset; use std::net::{IpAddr, SocketAddr, SocketAddrV6}; use uuid::Uuid; diff --git a/nexus/db-model/src/sled.rs b/nexus/db-model/src/sled.rs index 0fec6b6dd13..690a2bd3950 100644 --- a/nexus/db-model/src/sled.rs +++ b/nexus/db-model/src/sled.rs @@ -12,7 +12,6 @@ use crate::sled_policy::DbSledPolicy; use chrono::{DateTime, Utc}; use db_macros::Asset; use nexus_db_schema::schema::{physical_disk, sled, zpool}; -use nexus_sled_agent_shared::inventory::SledRole; use nexus_types::deployment::execution; use nexus_types::{ external_api::{shared, views}, @@ -21,6 +20,7 @@ use nexus_types::{ }; use omicron_uuid_kinds::SledKind; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::SledRole; use std::net::Ipv6Addr; use std::net::SocketAddrV6; use uuid::Uuid; diff --git a/nexus/db-model/src/sled_cpu_family.rs b/nexus/db-model/src/sled_cpu_family.rs index ab6aafee22d..e8b6908ad64 100644 --- a/nexus/db-model/src/sled_cpu_family.rs +++ b/nexus/db-model/src/sled_cpu_family.rs @@ -46,9 +46,9 @@ impl SledCpuFamily { } } -impl From for SledCpuFamily { - fn from(value: nexus_sled_agent_shared::inventory::SledCpuFamily) -> Self { - use nexus_sled_agent_shared::inventory::SledCpuFamily as InputFamily; +impl From for SledCpuFamily { + fn from(value: sled_agent_types::inventory::SledCpuFamily) -> Self { + use sled_agent_types::inventory::SledCpuFamily as InputFamily; match value { InputFamily::Unknown => Self::Unknown, InputFamily::AmdMilan => Self::AmdMilan, @@ -58,7 +58,7 @@ impl From for SledCpuFamily { } } -impl From for nexus_sled_agent_shared::inventory::SledCpuFamily { +impl From for sled_agent_types::inventory::SledCpuFamily { fn from(value: SledCpuFamily) -> Self { match value { SledCpuFamily::Unknown => Self::Unknown, diff --git a/nexus/db-queries/Cargo.toml b/nexus/db-queries/Cargo.toml index 314f9614a91..4e8d59ae91a 100644 --- a/nexus/db-queries/Cargo.toml +++ b/nexus/db-queries/Cargo.toml @@ -67,8 +67,8 @@ nexus-db-fixed-data.workspace = true nexus-db-model.workspace = true nexus-db-lookup.workspace = true nexus-db-schema.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true +sled-agent-types.workspace = true omicron-cockroach-metrics.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true diff --git a/nexus/db-queries/src/db/datastore/deployment/external_networking.rs b/nexus/db-queries/src/db/datastore/deployment/external_networking.rs index 35daaf60ac9..f142f5e1715 100644 --- a/nexus/db-queries/src/db/datastore/deployment/external_networking.rs +++ b/nexus/db-queries/src/db/datastore/deployment/external_networking.rs @@ -15,7 +15,6 @@ use nexus_db_lookup::DbConnection; use nexus_db_model::IncompleteNetworkInterface; use nexus_db_model::IpConfig; use nexus_db_model::IpPool; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintZoneConfig; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::BlueprintZoneType; @@ -28,6 +27,7 @@ use omicron_common::api::internal::shared::NetworkInterfaceKind; use omicron_common::api::internal::shared::PrivateIpConfig; use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::OmicronZoneUuid; +use sled_agent_types::inventory::ZoneKind; use slog::Logger; use slog::debug; use slog::error; @@ -529,7 +529,6 @@ mod tests { use nexus_reconfigurator_planning::blueprint_editor::ExternalNetworkingAllocator; use nexus_reconfigurator_planning::example::ExampleSystemBuilder; use nexus_reconfigurator_planning::planner::PlannerRng; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_types::deployment::BlueprintSource; use nexus_types::deployment::BlueprintTarget; use nexus_types::deployment::BlueprintZoneConfig; @@ -555,6 +554,7 @@ mod tests { use omicron_test_utils::dev; use omicron_uuid_kinds::ExternalIpUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZoneDataset; use std::collections::BTreeSet; use std::net::IpAddr; use std::net::SocketAddr; diff --git a/nexus/db-queries/src/db/datastore/external_ip.rs b/nexus/db-queries/src/db/datastore/external_ip.rs index 54e57b2ec25..cb6f55786fd 100644 --- a/nexus/db-queries/src/db/datastore/external_ip.rs +++ b/nexus/db-queries/src/db/datastore/external_ip.rs @@ -40,7 +40,6 @@ use nexus_db_model::FloatingIpUpdate; use nexus_db_model::Instance; use nexus_db_model::IpAttachState; use nexus_db_model::IpVersion; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::OmicronZoneExternalIp; use nexus_types::identity::Resource; use omicron_common::api::external::CreateResult; @@ -57,6 +56,7 @@ use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::InstanceUuid; use omicron_uuid_kinds::OmicronZoneUuid; use ref_cast::RefCast; +use sled_agent_types::inventory::ZoneKind; use std::net::IpAddr; use uuid::Uuid; diff --git a/nexus/db-queries/src/db/datastore/inventory.rs b/nexus/db-queries/src/db/datastore/inventory.rs index c79ac2d7228..bb9f7d4da80 100644 --- a/nexus/db-queries/src/db/datastore/inventory.rs +++ b/nexus/db-queries/src/db/datastore/inventory.rs @@ -85,16 +85,6 @@ use nexus_db_schema::enums::{ CabooseWhichEnum, InvConfigReconcilerStatusKindEnum, }; use nexus_db_schema::enums::{HwPowerStateEnum, InvZoneManifestSourceEnum}; -use nexus_sled_agent_shared::inventory::BootPartitionContents; -use nexus_sled_agent_shared::inventory::BootPartitionDetails; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::MupdateOverrideNonBootInventory; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OrphanedDataset; -use nexus_sled_agent_shared::inventory::ZoneArtifactInventory; -use nexus_sled_agent_shared::inventory::ZoneManifestNonBootInventory; use nexus_types::inventory::BaseboardId; use nexus_types::inventory::CockroachStatus; use nexus_types::inventory::Collection; @@ -115,6 +105,16 @@ use omicron_uuid_kinds::OmicronSledConfigUuid; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::BootPartitionContents; +use sled_agent_types::inventory::BootPartitionDetails; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::MupdateOverrideNonBootInventory; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::OrphanedDataset; +use sled_agent_types::inventory::ZoneArtifactInventory; +use sled_agent_types::inventory::ZoneManifestNonBootInventory; use slog_error_chain::InlineErrorChain; use std::collections::BTreeMap; use std::collections::BTreeSet; @@ -4461,17 +4461,6 @@ mod test { use nexus_inventory::examples::Representative; use nexus_inventory::examples::representative; use nexus_inventory::now_db_precision; - use nexus_sled_agent_shared::inventory::BootPartitionContents; - use nexus_sled_agent_shared::inventory::BootPartitionDetails; - use nexus_sled_agent_shared::inventory::OrphanedDataset; - use nexus_sled_agent_shared::inventory::{ - BootImageHeader, RemoveMupdateOverrideBootSuccessInventory, - RemoveMupdateOverrideInventory, - }; - use nexus_sled_agent_shared::inventory::{ - ConfigReconcilerInventory, ConfigReconcilerInventoryResult, - ConfigReconcilerInventoryStatus, OmicronZoneImageSource, - }; use nexus_test_utils::db::ALLOW_FULL_TABLE_SCAN_SQL; use nexus_types::inventory::CabooseWhich; use nexus_types::inventory::RotPageWhich; @@ -4487,6 +4476,17 @@ mod test { ZpoolUuid, }; use pretty_assertions::assert_eq; + use sled_agent_types::inventory::BootPartitionContents; + use sled_agent_types::inventory::BootPartitionDetails; + use sled_agent_types::inventory::OrphanedDataset; + use sled_agent_types::inventory::{ + BootImageHeader, RemoveMupdateOverrideBootSuccessInventory, + RemoveMupdateOverrideInventory, + }; + use sled_agent_types::inventory::{ + ConfigReconcilerInventory, ConfigReconcilerInventoryResult, + ConfigReconcilerInventoryStatus, OmicronZoneImageSource, + }; use std::num::NonZeroU32; use std::time::Duration; use tufaceous_artifact::ArtifactHash; diff --git a/nexus/db-queries/src/db/datastore/ip_pool.rs b/nexus/db-queries/src/db/datastore/ip_pool.rs index b2849277603..c829ba4befc 100644 --- a/nexus/db-queries/src/db/datastore/ip_pool.rs +++ b/nexus/db-queries/src/db/datastore/ip_pool.rs @@ -1984,7 +1984,6 @@ mod test { use nexus_db_model::{ IpPoolIdentity, IpPoolReservationType, IpPoolType, IpVersion, }; - use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::{ OmicronZoneExternalFloatingIp, OmicronZoneExternalIp, }; @@ -2000,6 +1999,7 @@ mod test { use omicron_uuid_kinds::{ ExternalIpUuid, GenericUuid as _, OmicronZoneUuid, }; + use sled_agent_types::inventory::ZoneKind; #[tokio::test] async fn test_default_ip_pools() { diff --git a/nexus/db-queries/src/db/datastore/physical_disk.rs b/nexus/db-queries/src/db/datastore/physical_disk.rs index 0f150bfc3a8..ca1957e07d7 100644 --- a/nexus/db-queries/src/db/datastore/physical_disk.rs +++ b/nexus/db-queries/src/db/datastore/physical_disk.rs @@ -340,15 +340,15 @@ mod test { use crate::db::pub_test_utils::helpers::SledUpdateBuilder; use dropshot::PaginationOrder; use nexus_db_lookup::LookupPath; - use nexus_sled_agent_shared::inventory::{ - Baseboard, ConfigReconcilerInventoryStatus, Inventory, InventoryDisk, - SledCpuFamily, SledRole, ZoneImageResolverInventory, - }; use nexus_types::identity::Asset; use omicron_common::api::external::ByteCount; use omicron_common::disk::{DiskIdentity, DiskVariant}; use omicron_test_utils::dev; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::{ + Baseboard, ConfigReconcilerInventoryStatus, Inventory, InventoryDisk, + SledCpuFamily, SledRole, ZoneImageResolverInventory, + }; use std::num::NonZeroU32; async fn create_test_sled(db: &DataStore) -> Sled { diff --git a/nexus/db-queries/src/db/queries/external_ip.rs b/nexus/db-queries/src/db/queries/external_ip.rs index 332ed5c3b65..71e595a8ea7 100644 --- a/nexus/db-queries/src/db/queries/external_ip.rs +++ b/nexus/db-queries/src/db/queries/external_ip.rs @@ -823,7 +823,6 @@ mod tests { use nexus_db_model::IpPoolResourceType; use nexus_db_model::IpVersion; use nexus_db_model::Name; - use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::OmicronZoneExternalFloatingIp; use nexus_types::deployment::OmicronZoneExternalIp; use nexus_types::deployment::OmicronZoneExternalSnatIp; @@ -838,6 +837,7 @@ mod tests { use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::InstanceUuid; use omicron_uuid_kinds::OmicronZoneUuid; + use sled_agent_types::inventory::ZoneKind; use std::net::IpAddr; use std::net::Ipv4Addr; use std::net::Ipv6Addr; diff --git a/nexus/inventory/Cargo.toml b/nexus/inventory/Cargo.toml index d7adeb05784..8075de9b4b3 100644 --- a/nexus/inventory/Cargo.toml +++ b/nexus/inventory/Cargo.toml @@ -23,7 +23,7 @@ gateway-messages.workspace = true gateway-types.workspace = true iddqd.workspace = true itertools.workspace = true -nexus-sled-agent-shared.workspace = true +sled-agent-types-versions.workspace = true nexus-types.workspace = true ntp-admin-client.workspace = true omicron-common.workspace = true diff --git a/nexus/inventory/src/builder.rs b/nexus/inventory/src/builder.rs index 1b714f3e17f..83b893657a4 100644 --- a/nexus/inventory/src/builder.rs +++ b/nexus/inventory/src/builder.rs @@ -17,8 +17,6 @@ use cockroach_admin_types::NodeId; use gateway_client::types::SpComponentCaboose; use gateway_client::types::SpState; use iddqd::IdOrdMap; -use nexus_sled_agent_shared::inventory::Baseboard; -use nexus_sled_agent_shared::inventory::Inventory; use nexus_types::inventory::BaseboardId; use nexus_types::inventory::Caboose; use nexus_types::inventory::CabooseFound; @@ -41,6 +39,8 @@ use omicron_cockroach_metrics::CockroachMetric; use omicron_cockroach_metrics::PrometheusMetrics; use omicron_common::disk::M2Slot; use omicron_uuid_kinds::CollectionKind; +use sled_agent_types::inventory::Baseboard; +use sled_agent_types::inventory::Inventory; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::hash::Hash; @@ -735,10 +735,9 @@ impl CollectionBuilder { /// Returns all zones of a kind from the ledgers of observed sleds pub fn ledgered_zones_of_kind( &self, - kind: nexus_sled_agent_shared::inventory::ZoneKind, - ) -> impl Iterator< - Item = &nexus_sled_agent_shared::inventory::OmicronZoneConfig, - > + '_ { + kind: sled_agent_types::inventory::ZoneKind, + ) -> impl Iterator + '_ + { self.sleds.iter().flat_map(move |sled| { sled.ledgered_sled_config.as_ref().into_iter().flat_map( move |sled_config| { @@ -756,10 +755,9 @@ impl CollectionBuilder { /// created by the sled reconciliation process pub fn last_reconciled_zones_of_kind( &self, - kind: nexus_sled_agent_shared::inventory::ZoneKind, - ) -> impl Iterator< - Item = &nexus_sled_agent_shared::inventory::OmicronZoneConfig, - > + '_ { + kind: sled_agent_types::inventory::ZoneKind, + ) -> impl Iterator + '_ + { self.sleds.iter().flat_map(move |sled| { sled.last_reconciliation.as_ref().into_iter().flat_map( move |sled_config| { @@ -798,7 +796,6 @@ mod test { use gateway_client::types::SpComponentCaboose; use gateway_client::types::SpState; use gateway_types::rot::RotSlot; - use nexus_sled_agent_shared::inventory::SledRole; use nexus_types::inventory::BaseboardId; use nexus_types::inventory::Caboose; use nexus_types::inventory::CabooseWhich; @@ -806,6 +803,7 @@ mod test { use nexus_types::inventory::RotPageWhich; use nexus_types::inventory::SpType; use omicron_common::api::external::ByteCount; + use sled_agent_types::inventory::SledRole; // Verify the contents of an empty collection. #[test] diff --git a/nexus/inventory/src/collector.rs b/nexus/inventory/src/collector.rs index 166681a9b5c..560f9021c58 100644 --- a/nexus/inventory/src/collector.rs +++ b/nexus/inventory/src/collector.rs @@ -13,8 +13,6 @@ use gateway_client::types::GetCfpaParams; use gateway_client::types::RotCfpaSlot; use gateway_messages::SpComponent; use itertools::Itertools; -use nexus_sled_agent_shared::inventory::OmicronZoneType; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::inventory::CabooseWhich; use nexus_types::inventory::Collection; use nexus_types::inventory::InternalDnsGenerationStatus; @@ -25,6 +23,8 @@ use omicron_cockroach_metrics::CockroachClusterAdminClient; use omicron_common::address::NTP_ADMIN_PORT; use omicron_common::disk::M2Slot; use omicron_uuid_kinds::OmicronZoneUuid; +use sled_agent_types::inventory::OmicronZoneType; +use sled_agent_types::inventory::ZoneKind; use slog::Logger; use slog::o; use slog::{debug, error}; @@ -721,13 +721,6 @@ mod test { use gateway_messages::SpPort; use iddqd::IdOrdMap; use iddqd::id_ord_map; - use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; - use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; - use nexus_sled_agent_shared::inventory::OmicronSledConfig; - use nexus_sled_agent_shared::inventory::OmicronZoneConfig; - use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; - use nexus_sled_agent_shared::inventory::OmicronZoneType; - use nexus_sled_agent_shared::inventory::SledCpuFamily; use nexus_types::inventory::Collection; use omicron_cockroach_metrics::CockroachClusterAdminClient; use omicron_common::api::external::Generation; @@ -736,6 +729,13 @@ mod test { use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; + use sled_agent_types::inventory::HostPhase2DesiredSlots; + use sled_agent_types::inventory::OmicronSledConfig; + use sled_agent_types::inventory::OmicronZoneConfig; + use sled_agent_types::inventory::OmicronZoneImageSource; + use sled_agent_types::inventory::OmicronZoneType; + use sled_agent_types::inventory::SledCpuFamily; use slog::o; use std::net::Ipv6Addr; use std::net::SocketAddrV6; diff --git a/nexus/inventory/src/examples.rs b/nexus/inventory/src/examples.rs index c3ae457d84f..579c4519b35 100644 --- a/nexus/inventory/src/examples.rs +++ b/nexus/inventory/src/examples.rs @@ -15,22 +15,6 @@ use gateway_client::types::SpComponentCaboose; use gateway_client::types::SpState; use gateway_types::rot::RotSlot; use iddqd::id_ord_map; -use nexus_sled_agent_shared::inventory::Baseboard; -use nexus_sled_agent_shared::inventory::BootImageHeader; -use nexus_sled_agent_shared::inventory::BootPartitionDetails; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::Inventory; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::InventoryDisk; -use nexus_sled_agent_shared::inventory::InventoryZpool; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OmicronZonesConfig; -use nexus_sled_agent_shared::inventory::OrphanedDataset; -use nexus_sled_agent_shared::inventory::SledCpuFamily; -use nexus_sled_agent_shared::inventory::SledRole; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; use nexus_types::inventory::BaseboardId; use nexus_types::inventory::CabooseWhich; use nexus_types::inventory::InternalDnsGenerationStatus; @@ -52,8 +36,22 @@ use omicron_uuid_kinds::DatasetUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; -use sled_agent_types::inventory::v9::OmicronZonesConfig as OmicronZonesConfigV9; -use sled_agent_types::inventory::v10::OmicronZonesConfig as OmicronZonesConfigV10; +use sled_agent_types::inventory::Baseboard; +use sled_agent_types::inventory::BootImageHeader; +use sled_agent_types::inventory::BootPartitionDetails; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::HostPhase2DesiredSlots; +use sled_agent_types::inventory::Inventory; +use sled_agent_types::inventory::InventoryDataset; +use sled_agent_types::inventory::InventoryDisk; +use sled_agent_types::inventory::InventoryZpool; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::OmicronZonesConfig; +use sled_agent_types::inventory::OrphanedDataset; +use sled_agent_types::inventory::SledCpuFamily; +use sled_agent_types::inventory::SledRole; +use sled_agent_types::inventory::ZoneImageResolverInventory; use sled_agent_types::zone_images::MupdateOverrideNonBootInfo; use sled_agent_types::zone_images::MupdateOverrideNonBootMismatch; use sled_agent_types::zone_images::MupdateOverrideNonBootResult; @@ -65,6 +63,8 @@ use sled_agent_types::zone_images::ZoneManifestNonBootMismatch; use sled_agent_types::zone_images::ZoneManifestNonBootResult; use sled_agent_types::zone_images::ZoneManifestReadError; use sled_agent_types::zone_images::ZoneManifestStatus; +use sled_agent_types_versions::v4::inventory::OmicronZonesConfig as OmicronZonesConfigV4; +use sled_agent_types_versions::v10::inventory::OmicronZonesConfig as OmicronZonesConfigV10; use sled_agent_zone_images_examples::BOOT_PATHS; use sled_agent_zone_images_examples::NON_BOOT_2_PATHS; use sled_agent_zone_images_examples::NON_BOOT_2_UUID; @@ -384,8 +384,8 @@ pub fn representative() -> Representative { let sled16_data = include_str!("../example-data/madrid-sled16.json"); let sled17_data = include_str!("../example-data/madrid-sled17.json"); let extract_current_omicron_zones_config = |data: &str| { - let as_v9: OmicronZonesConfigV9 = serde_json::from_str(data).unwrap(); - OmicronZonesConfigV10::try_from(as_v9) + let as_v4: OmicronZonesConfigV4 = serde_json::from_str(data).unwrap(); + OmicronZonesConfigV10::try_from(as_v4) .and_then(OmicronZonesConfig::try_from) }; let sled14 = extract_current_omicron_zones_config(sled14_data).unwrap(); diff --git a/nexus/mgs-updates/Cargo.toml b/nexus/mgs-updates/Cargo.toml index b1600ab6e6a..557abfc91ec 100644 --- a/nexus/mgs-updates/Cargo.toml +++ b/nexus/mgs-updates/Cargo.toml @@ -15,7 +15,6 @@ gateway-messages.workspace = true iddqd.workspace = true internal-dns-resolver.workspace = true internal-dns-types.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true @@ -24,6 +23,7 @@ repo-depot-client.workspace = true reqwest.workspace = true sha2.workspace = true sled-agent-client.workspace = true +sled-agent-types.workspace = true slog.workspace = true slog-error-chain.workspace = true thiserror.workspace = true diff --git a/nexus/mgs-updates/src/host_phase1_updater.rs b/nexus/mgs-updates/src/host_phase1_updater.rs index 84af2ba81d1..b6578206860 100644 --- a/nexus/mgs-updates/src/host_phase1_updater.rs +++ b/nexus/mgs-updates/src/host_phase1_updater.rs @@ -138,12 +138,12 @@ use futures::future::BoxFuture; use gateway_client::HostPhase1HashError; use gateway_client::SpComponent; use gateway_client::types::SpComponentFirmwareSlot; -use nexus_sled_agent_shared::inventory::BootPartitionContents; use nexus_types::deployment::PendingMgsUpdate; use nexus_types::deployment::PendingMgsUpdateHostPhase1Details; use nexus_types::inventory::SpType; use omicron_common::disk::M2Slot; use sled_agent_client::Client as SledAgentClient; +use sled_agent_types::inventory::BootPartitionContents; use slog::Logger; use slog::debug; use slog_error_chain::InlineErrorChain; diff --git a/nexus/mgs-updates/src/test_util/host_phase_2_test_state.rs b/nexus/mgs-updates/src/test_util/host_phase_2_test_state.rs index ad4e3139e32..30a59a426dd 100644 --- a/nexus/mgs-updates/src/test_util/host_phase_2_test_state.rs +++ b/nexus/mgs-updates/src/test_util/host_phase_2_test_state.rs @@ -9,10 +9,10 @@ use anyhow::Context as _; use dropshot::ConfigDropshot; use dropshot::HttpServer; use dropshot::ServerBuilder; -use nexus_sled_agent_shared::inventory::Baseboard; -use nexus_sled_agent_shared::inventory::SledRole; use omicron_common::disk::M2Slot; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::Baseboard; +use sled_agent_types::inventory::SledRole; use slog::Logger; use sp_sim::GimletPowerState; use std::net::SocketAddr; @@ -197,20 +197,6 @@ mod api_impl { use dropshot::StreamingBody; use dropshot::TypedBody; use iddqd::IdOrdMap; - use nexus_sled_agent_shared::inventory::BootImageHeader; - use nexus_sled_agent_shared::inventory::BootPartitionContents; - use nexus_sled_agent_shared::inventory::BootPartitionDetails; - use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; - use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; - use nexus_sled_agent_shared::inventory::HostPhase2DesiredContents; - use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; - use nexus_sled_agent_shared::inventory::Inventory; - use nexus_sled_agent_shared::inventory::MupdateOverrideInventory; - use nexus_sled_agent_shared::inventory::OmicronSledConfig; - use nexus_sled_agent_shared::inventory::SledCpuFamily; - use nexus_sled_agent_shared::inventory::SledRole; - use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; - use nexus_sled_agent_shared::inventory::ZoneManifestInventory; use omicron_common::api::external::Generation; use omicron_common::api::internal::nexus::DiskRuntimeState; use omicron_common::api::internal::nexus::SledVmmState; @@ -220,23 +206,66 @@ mod api_impl { use omicron_common::api::internal::shared::{ ResolvedVpcRouteSet, ResolvedVpcRouteState, SwitchPorts, }; - use sled_agent_api::*; + use sled_agent_types::artifact::ArtifactConfig; + use sled_agent_types::artifact::ArtifactCopyFromDepotBody; + use sled_agent_types::artifact::ArtifactCopyFromDepotResponse; + use sled_agent_types::artifact::ArtifactListResponse; + use sled_agent_types::artifact::ArtifactPathParam; + use sled_agent_types::artifact::ArtifactPutResponse; + use sled_agent_types::artifact::ArtifactQueryParam; use sled_agent_types::bootstore::BootstoreStatus; + use sled_agent_types::dataset::LocalStorageDatasetEnsureRequest; + use sled_agent_types::dataset::LocalStoragePathParam; + use sled_agent_types::debug::ChickenSwitchDestroyOrphanedDatasets; + use sled_agent_types::debug::OperatorSwitchZonePolicy; + use sled_agent_types::diagnostics::SledDiagnosticsLogsDownloadPathParm; + use sled_agent_types::diagnostics::SledDiagnosticsLogsDownloadQueryParam; use sled_agent_types::disk::DiskEnsureBody; + use sled_agent_types::disk::DiskPathParam; use sled_agent_types::early_networking::EarlyNetworkConfig; use sled_agent_types::firewall_rules::VpcFirewallRulesEnsureBody; + use sled_agent_types::instance::InstanceEnsureBody; use sled_agent_types::instance::InstanceExternalIpBody; use sled_agent_types::instance::InstanceMulticastBody; + use sled_agent_types::instance::VmmIssueDiskSnapshotRequestBody; + use sled_agent_types::instance::VmmIssueDiskSnapshotRequestPathParam; + use sled_agent_types::instance::VmmIssueDiskSnapshotRequestResponse; + use sled_agent_types::instance::VmmPathParam; use sled_agent_types::instance::VmmPutStateBody; use sled_agent_types::instance::VmmPutStateResponse; use sled_agent_types::instance::VmmUnregisterResponse; + use sled_agent_types::instance::VpcPathParam; + use sled_agent_types::inventory::BootImageHeader; + use sled_agent_types::inventory::BootPartitionContents; + use sled_agent_types::inventory::BootPartitionDetails; + use sled_agent_types::inventory::ConfigReconcilerInventory; + use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; + use sled_agent_types::inventory::HostPhase2DesiredContents; + use sled_agent_types::inventory::HostPhase2DesiredSlots; + use sled_agent_types::inventory::Inventory; + use sled_agent_types::inventory::MupdateOverrideInventory; + use sled_agent_types::inventory::OmicronSledConfig; + use sled_agent_types::inventory::SledCpuFamily; + use sled_agent_types::inventory::SledRole; + use sled_agent_types::inventory::ZoneImageResolverInventory; + use sled_agent_types::inventory::ZoneManifestInventory; use sled_agent_types::probes::ProbeSet; use sled_agent_types::sled::AddSledRequest; + use sled_agent_types::support_bundle::RangeRequestHeaders; + use sled_agent_types::support_bundle::SupportBundleFilePathParam; + use sled_agent_types::support_bundle::SupportBundleFinalizeQueryParams; + use sled_agent_types::support_bundle::SupportBundleListPathParam; + use sled_agent_types::support_bundle::SupportBundleMetadata; + use sled_agent_types::support_bundle::SupportBundlePathParam; + use sled_agent_types::support_bundle::SupportBundleTransferQueryParams; use sled_agent_types::zone_bundle::BundleUtilization; use sled_agent_types::zone_bundle::CleanupContext; + use sled_agent_types::zone_bundle::CleanupContextUpdate; use sled_agent_types::zone_bundle::CleanupCount; + use sled_agent_types::zone_bundle::ZoneBundleFilter; use sled_agent_types::zone_bundle::ZoneBundleId; use sled_agent_types::zone_bundle::ZoneBundleMetadata; + use sled_agent_types::zone_bundle::ZonePathParam; use sled_diagnostics::SledDiagnosticsQueryOutput; use std::collections::BTreeMap; use std::time::Duration; @@ -524,7 +553,7 @@ mod api_impl { unimplemented!() } - async fn sled_role_get( + async fn sled_role_get_v1( _rqctx: RequestContext, ) -> Result, HttpError> { unimplemented!() @@ -533,7 +562,7 @@ mod api_impl { async fn vmm_register( _rqctx: RequestContext, _path_params: Path, - _body: TypedBody, + _body: TypedBody, ) -> Result, HttpError> { unimplemented!() } @@ -854,7 +883,7 @@ mod api_impl { unimplemented!() } - async fn chicken_switch_destroy_orphaned_datasets_get( + async fn chicken_switch_destroy_orphaned_datasets_get_v1( _request_context: RequestContext, ) -> Result< HttpResponseOk, @@ -863,7 +892,7 @@ mod api_impl { unimplemented!() } - async fn chicken_switch_destroy_orphaned_datasets_put( + async fn chicken_switch_destroy_orphaned_datasets_put_v1( _request_context: RequestContext, _body: TypedBody, ) -> Result { diff --git a/nexus/reconfigurator/blippy/Cargo.toml b/nexus/reconfigurator/blippy/Cargo.toml index 293970aedef..6a19e1ece1f 100644 --- a/nexus/reconfigurator/blippy/Cargo.toml +++ b/nexus/reconfigurator/blippy/Cargo.toml @@ -7,8 +7,8 @@ edition.workspace = true workspace = true [dependencies] -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true +sled-agent-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/nexus/reconfigurator/blippy/src/checks.rs b/nexus/reconfigurator/blippy/src/checks.rs index 23e3248f727..eec34c2a34d 100644 --- a/nexus/reconfigurator/blippy/src/checks.rs +++ b/nexus/reconfigurator/blippy/src/checks.rs @@ -7,7 +7,6 @@ use crate::blippy::BlueprintKind; use crate::blippy::PlanningInputKind; use crate::blippy::Severity; use crate::blippy::SledKind; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintDatasetConfig; use nexus_types::deployment::BlueprintDatasetDisposition; use nexus_types::deployment::BlueprintHostPhase2DesiredContents; @@ -30,6 +29,7 @@ use omicron_common::disk::M2Slot; use omicron_uuid_kinds::MupdateOverrideUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; +use sled_agent_types::inventory::ZoneKind; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::btree_map::Entry; diff --git a/nexus/reconfigurator/execution/Cargo.toml b/nexus/reconfigurator/execution/Cargo.toml index 76ef38ed929..30339ca61b4 100644 --- a/nexus/reconfigurator/execution/Cargo.toml +++ b/nexus/reconfigurator/execution/Cargo.toml @@ -29,13 +29,13 @@ nexus-db-model.workspace = true nexus-db-queries.workspace = true nexus-mgs-updates.workspace = true nexus-networking.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true oxnet.workspace = true reqwest.workspace = true sled-agent-client.workspace = true +sled-agent-types.workspace = true slog.workspace = true slog-error-chain.workspace = true tokio.workspace = true diff --git a/nexus/reconfigurator/execution/src/clickhouse.rs b/nexus/reconfigurator/execution/src/clickhouse.rs index d0395d4659e..b5ec2beb4c5 100644 --- a/nexus/reconfigurator/execution/src/clickhouse.rs +++ b/nexus/reconfigurator/execution/src/clickhouse.rs @@ -383,13 +383,13 @@ mod test { use clickhouse_admin_types::ClickhouseHost; use clickhouse_admin_types::KeeperId; use clickhouse_admin_types::ServerId; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_types::deployment::BlueprintZoneConfig; use nexus_types::deployment::BlueprintZoneImageSource; use nexus_types::deployment::BlueprintZoneType; use nexus_types::deployment::blueprint_zone_type; use nexus_types::inventory::ZpoolName; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZoneDataset; use std::collections::BTreeSet; fn test_data() -> (Vec, ClickhouseClusterConfig) { diff --git a/nexus/reconfigurator/execution/src/dns.rs b/nexus/reconfigurator/execution/src/dns.rs index 2e6b2264d3b..202188c2621 100644 --- a/nexus/reconfigurator/execution/src/dns.rs +++ b/nexus/reconfigurator/execution/src/dns.rs @@ -333,11 +333,6 @@ mod test { use nexus_reconfigurator_planning::blueprint_editor::ExternalNetworkingAllocator; use nexus_reconfigurator_planning::example::ExampleSystemBuilder; use nexus_reconfigurator_planning::planner::PlannerRng; - use nexus_sled_agent_shared::inventory::OmicronZoneConfig; - use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; - use nexus_sled_agent_shared::inventory::OmicronZoneType; - use nexus_sled_agent_shared::inventory::SledRole; - use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_test_utils::resource_helpers::DiskTest; use nexus_test_utils::resource_helpers::create_silo; use nexus_test_utils_macros::nexus_test; @@ -384,6 +379,11 @@ mod test { use omicron_uuid_kinds::ExternalIpUuid; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZoneConfig; + use sled_agent_types::inventory::OmicronZoneImageSource; + use sled_agent_types::inventory::OmicronZoneType; + use sled_agent_types::inventory::SledRole; + use sled_agent_types::inventory::ZoneKind; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::HashMap; diff --git a/nexus/reconfigurator/execution/src/omicron_sled_config.rs b/nexus/reconfigurator/execution/src/omicron_sled_config.rs index 5a3758976d9..653302dc9d1 100644 --- a/nexus/reconfigurator/execution/src/omicron_sled_config.rs +++ b/nexus/reconfigurator/execution/src/omicron_sled_config.rs @@ -81,8 +81,6 @@ pub(crate) async fn deploy_sled_configs( mod tests { use super::*; use iddqd::id_ord_map; - use nexus_sled_agent_shared::inventory::OmicronZonesConfig; - use nexus_sled_agent_shared::inventory::SledRole; use nexus_test_utils_macros::nexus_test; use nexus_types::deployment::BlueprintDatasetConfig; use nexus_types::deployment::BlueprintDatasetDisposition; @@ -110,6 +108,8 @@ mod tests { use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZonesConfig; + use sled_agent_types::inventory::SledRole; use std::net::Ipv6Addr; use std::net::SocketAddr; diff --git a/nexus/reconfigurator/execution/src/omicron_zones.rs b/nexus/reconfigurator/execution/src/omicron_zones.rs index 74e4625358d..2dfa9772faa 100644 --- a/nexus/reconfigurator/execution/src/omicron_zones.rs +++ b/nexus/reconfigurator/execution/src/omicron_zones.rs @@ -305,7 +305,6 @@ mod test { use httptest::Expectation; use httptest::matchers::{all_of, json_decoded, request}; use httptest::responders::{json_encoded, status_code}; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_test_utils_macros::nexus_test; use nexus_types::deployment::{ BlueprintZoneImageSource, blueprint_zone_type, @@ -315,6 +314,7 @@ mod test { use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZoneDataset; use std::iter; use uuid::Uuid; diff --git a/nexus/reconfigurator/planning/Cargo.toml b/nexus/reconfigurator/planning/Cargo.toml index 4188fbb1593..49714064cd5 100644 --- a/nexus/reconfigurator/planning/Cargo.toml +++ b/nexus/reconfigurator/planning/Cargo.toml @@ -27,7 +27,6 @@ itertools.workspace = true nexus-config.workspace = true nexus-inventory.workspace = true nexus-reconfigurator-blippy.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true @@ -36,6 +35,7 @@ oxnet.workspace = true rand.workspace = true semver.workspace = true sled-agent-client.workspace = true +sled-agent-types.workspace = true sled-hardware-types.workspace = true slog.workspace = true slog-error-chain.workspace = true diff --git a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs index 2ca9f430546..5aa042c4c92 100644 --- a/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs +++ b/nexus/reconfigurator/planning/src/blueprint_builder/builder.rs @@ -24,9 +24,6 @@ use iddqd::IdOrdMap; use iddqd::id_upcast; use itertools::Either; use nexus_inventory::now_db_precision; -use nexus_sled_agent_shared::inventory::MupdateOverrideBootInventory; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::Blueprint; use nexus_types::deployment::BlueprintDatasetDisposition; use nexus_types::deployment::BlueprintHostPhase2DesiredContents; @@ -73,6 +70,9 @@ use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; +use sled_agent_types::inventory::MupdateOverrideBootInventory; +use sled_agent_types::inventory::OmicronZoneDataset; +use sled_agent_types::inventory::ZoneKind; use slog::Logger; use slog::debug; use slog::error; diff --git a/nexus/reconfigurator/planning/src/blueprint_editor/allocators/external_networking.rs b/nexus/reconfigurator/planning/src/blueprint_editor/allocators/external_networking.rs index 3a3c31686cf..c428ec39c7f 100644 --- a/nexus/reconfigurator/planning/src/blueprint_editor/allocators/external_networking.rs +++ b/nexus/reconfigurator/planning/src/blueprint_editor/allocators/external_networking.rs @@ -5,7 +5,6 @@ use anyhow::bail; use debug_ignore::DebugIgnore; use nexus_config::NUM_INITIAL_RESERVED_IP_ADDRESSES; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintZoneConfig; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::BlueprintZoneType; @@ -23,6 +22,7 @@ use omicron_common::api::external::MacAddr; use omicron_common::api::internal::shared::PrivateIpConfig; use omicron_common::api::internal::shared::PrivateIpConfigError; use omicron_common::api::internal::shared::SourceNatConfigError; +use sled_agent_types::inventory::ZoneKind; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::HashSet; @@ -677,7 +677,6 @@ impl TryFrom<(u16, u16)> for SnatPortRange { pub mod test { use super::*; use illumos_utils::zpool::ZpoolName; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::BlueprintZoneImageSource; use nexus_types::deployment::OmicronZoneExternalFloatingAddr; @@ -693,6 +692,7 @@ pub mod test { use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZoneDataset; use slog_error_chain::InlineErrorChain; use std::net::SocketAddr; use test_strategy::proptest; diff --git a/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor.rs b/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor.rs index e0a75b1b367..6c11d42d8d3 100644 --- a/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor.rs +++ b/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor.rs @@ -20,8 +20,6 @@ use iddqd::IdOrdMap; use iddqd::id_ord_map::Entry; use illumos_utils::zpool::ZpoolName; use itertools::Either; -use nexus_sled_agent_shared::inventory::MupdateOverrideBootInventory; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintDatasetConfig; use nexus_types::deployment::BlueprintDatasetDisposition; use nexus_types::deployment::BlueprintHostPhase2DesiredContents; @@ -47,6 +45,8 @@ use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::ZpoolUuid; use scalar::ScalarEditor; +use sled_agent_types::inventory::MupdateOverrideBootInventory; +use sled_agent_types::inventory::ZoneKind; use std::iter; use std::mem; use std::net::Ipv6Addr; diff --git a/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor/zones.rs b/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor/zones.rs index af2d1969df8..7f710aa7cf4 100644 --- a/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor/zones.rs +++ b/nexus/reconfigurator/planning/src/blueprint_editor/sled_editor/zones.rs @@ -5,13 +5,13 @@ use crate::blueprint_builder::EditCounts; use iddqd::IdOrdMap; use iddqd::id_ord_map::Entry; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintZoneConfig; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::BlueprintZoneImageSource; use omicron_common::api::external::Generation; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::ZpoolUuid; +use sled_agent_types::inventory::ZoneKind; #[derive(Debug, thiserror::Error)] pub enum ZonesEditError { diff --git a/nexus/reconfigurator/planning/src/example.rs b/nexus/reconfigurator/planning/src/example.rs index 81ab3c68199..efd690b2815 100644 --- a/nexus/reconfigurator/planning/src/example.rs +++ b/nexus/reconfigurator/planning/src/example.rs @@ -25,7 +25,6 @@ use camino::Utf8Path; use camino_tempfile::Utf8TempDir; use clap::Parser; use nexus_inventory::CollectionBuilderRng; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::Blueprint; use nexus_types::deployment::BlueprintArtifactVersion; use nexus_types::deployment::BlueprintHostPhase2DesiredContents; @@ -46,6 +45,7 @@ use omicron_common::policy::INTERNAL_DNS_REDUNDANCY; use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::SledKind; use omicron_uuid_kinds::VnicUuid; +use sled_agent_types::inventory::ZoneKind; use tufaceous_artifact::ArtifactHash; use tufaceous_artifact::ArtifactKind; use tufaceous_artifact::KnownArtifactKind; @@ -1017,7 +1017,6 @@ mod tests { use internal_dns_resolver::ResolveError; use internal_dns_resolver::Resolver; use internal_dns_types::names::ServiceName; - use nexus_sled_agent_shared::inventory::{OmicronZoneConfig, ZoneKind}; use nexus_types::deployment::BlueprintZoneConfig; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::execution::blueprint_internal_dns_config; @@ -1027,6 +1026,7 @@ mod tests { use omicron_common::address::get_sled_address; use omicron_common::api::external::Generation; use omicron_test_utils::dev::test_setup_log; + use sled_agent_types::inventory::{OmicronZoneConfig, ZoneKind}; use slog_error_chain::InlineErrorChain; use super::*; diff --git a/nexus/reconfigurator/planning/src/mgs_updates/test_helpers.rs b/nexus/reconfigurator/planning/src/mgs_updates/test_helpers.rs index fcd425aed1b..c934e51ab2b 100644 --- a/nexus/reconfigurator/planning/src/mgs_updates/test_helpers.rs +++ b/nexus/reconfigurator/planning/src/mgs_updates/test_helpers.rs @@ -15,18 +15,6 @@ use gateway_client::types::SpState; use gateway_types::rot::RotSlot; use iddqd::IdOrdItem; use iddqd::IdOrdMap; -use nexus_sled_agent_shared::inventory::Baseboard; -use nexus_sled_agent_shared::inventory::BootImageHeader; -use nexus_sled_agent_shared::inventory::BootPartitionContents; -use nexus_sled_agent_shared::inventory::BootPartitionDetails; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::Inventory; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::SledCpuFamily; -use nexus_sled_agent_shared::inventory::SledRole; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; use nexus_types::deployment::BlueprintArtifactVersion; use nexus_types::deployment::BlueprintHostPhase2DesiredContents; use nexus_types::deployment::ExpectedVersion; @@ -47,6 +35,18 @@ use omicron_common::api::external::TufRepoMeta; use omicron_common::disk::M2Slot; use omicron_common::update::ArtifactId; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::Baseboard; +use sled_agent_types::inventory::BootImageHeader; +use sled_agent_types::inventory::BootPartitionContents; +use sled_agent_types::inventory::BootPartitionDetails; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::HostPhase2DesiredSlots; +use sled_agent_types::inventory::Inventory; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::SledCpuFamily; +use sled_agent_types::inventory::SledRole; +use sled_agent_types::inventory::ZoneImageResolverInventory; use sled_hardware_types::COSMO_SLED_MODEL; use sled_hardware_types::GIMLET_SLED_MODEL; use sled_hardware_types::OxideSled; diff --git a/nexus/reconfigurator/planning/src/planner.rs b/nexus/reconfigurator/planning/src/planner.rs index 4d2a4896516..b12cbb8067d 100644 --- a/nexus/reconfigurator/planning/src/planner.rs +++ b/nexus/reconfigurator/planning/src/planner.rs @@ -27,10 +27,6 @@ use crate::planner::omicron_zone_placement::PlacementError; use iddqd::IdOrdMap; use itertools::Either; use itertools::Itertools; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; -use nexus_sled_agent_shared::inventory::OmicronZoneType; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::Blueprint; use nexus_types::deployment::BlueprintPhysicalDiskDisposition; use nexus_types::deployment::BlueprintSource; @@ -66,6 +62,10 @@ use omicron_common::disk::M2Slot; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types::inventory::OmicronZoneImageSource; +use sled_agent_types::inventory::OmicronZoneType; +use sled_agent_types::inventory::ZoneKind; use slog::error; use slog::{Logger, info, o, warn}; use slog_error_chain::InlineErrorChain; diff --git a/nexus/reconfigurator/planning/src/planner/image_source.rs b/nexus/reconfigurator/planning/src/planner/image_source.rs index 4ec2235a341..8f6b3631b04 100644 --- a/nexus/reconfigurator/planning/src/planner/image_source.rs +++ b/nexus/reconfigurator/planning/src/planner/image_source.rs @@ -6,10 +6,6 @@ use std::{collections::HashMap, fmt}; use anyhow::anyhow; use iddqd::{IdOrdItem, IdOrdMap, id_ord_map::RefMut, id_upcast}; -use nexus_sled_agent_shared::inventory::{ - BootPartitionContents, BootPartitionDetails, ZoneKind, - ZoneManifestBootInventory, -}; use nexus_types::{ deployment::{ BlueprintArtifactVersion, BlueprintHostPhase2DesiredContents, @@ -21,6 +17,10 @@ use nexus_types::{ }; use omicron_common::api::external::TufArtifactMeta; use omicron_uuid_kinds::{MupdateOverrideUuid, OmicronZoneUuid, SledUuid}; +use sled_agent_types::inventory::{ + BootPartitionContents, BootPartitionDetails, ZoneKind, + ZoneManifestBootInventory, +}; use slog::{debug, info, o, warn}; use tufaceous_artifact::ArtifactHash; diff --git a/nexus/reconfigurator/planning/src/planner/omicron_zone_placement.rs b/nexus/reconfigurator/planning/src/planner/omicron_zone_placement.rs index ffdf44a1c22..f39d7a5e895 100644 --- a/nexus/reconfigurator/planning/src/planner/omicron_zone_placement.rs +++ b/nexus/reconfigurator/planning/src/planner/omicron_zone_placement.rs @@ -4,9 +4,9 @@ //! Omicron zone placement decisions -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintZoneType; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::ZoneKind; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::mem; diff --git a/nexus/reconfigurator/planning/src/planner/zone_safety.rs b/nexus/reconfigurator/planning/src/planner/zone_safety.rs index 2b565783cae..ded59e5fc17 100644 --- a/nexus/reconfigurator/planning/src/planner/zone_safety.rs +++ b/nexus/reconfigurator/planning/src/planner/zone_safety.rs @@ -7,7 +7,6 @@ use crate::blueprint_builder::BlueprintBuilder; use itertools::Itertools; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::BlueprintZoneConfig; use nexus_types::deployment::BlueprintZoneDisposition; use nexus_types::deployment::CockroachdbUnsafeToShutdown; @@ -16,6 +15,7 @@ use nexus_types::deployment::ZoneUnsafeToShutdown; use nexus_types::inventory::Collection; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::SledUuid; +use sled_agent_types::inventory::ZoneKind; use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/nexus/reconfigurator/planning/src/system.rs b/nexus/reconfigurator/planning/src/system.rs index 3e88cbdd01d..ea57a079da9 100644 --- a/nexus/reconfigurator/planning/src/system.rs +++ b/nexus/reconfigurator/planning/src/system.rs @@ -16,20 +16,6 @@ use indexmap::IndexMap; use ipnet::Ipv6Net; use ipnet::Ipv6Subnets; use nexus_inventory::CollectionBuilder; -use nexus_sled_agent_shared::inventory::Baseboard; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::Inventory; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::InventoryDisk; -use nexus_sled_agent_shared::inventory::InventoryZpool; -use nexus_sled_agent_shared::inventory::MupdateOverrideBootInventory; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::SledCpuFamily; -use nexus_sled_agent_shared::inventory::SledRole; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; -use nexus_sled_agent_shared::inventory::ZoneKind; -use nexus_sled_agent_shared::inventory::ZoneManifestBootInventory; use nexus_types::deployment::Blueprint; use nexus_types::deployment::ClickhousePolicy; use nexus_types::deployment::CockroachDbClusterVersion; @@ -73,6 +59,20 @@ use omicron_uuid_kinds::MupdateOverrideUuid; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; +use sled_agent_types::inventory::Baseboard; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::Inventory; +use sled_agent_types::inventory::InventoryDataset; +use sled_agent_types::inventory::InventoryDisk; +use sled_agent_types::inventory::InventoryZpool; +use sled_agent_types::inventory::MupdateOverrideBootInventory; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::SledCpuFamily; +use sled_agent_types::inventory::SledRole; +use sled_agent_types::inventory::ZoneImageResolverInventory; +use sled_agent_types::inventory::ZoneKind; +use sled_agent_types::inventory::ZoneManifestBootInventory; use sled_hardware_types::GIMLET_SLED_MODEL; use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/nexus/reconfigurator/planning/tests/integration_tests/planner.rs b/nexus/reconfigurator/planning/tests/integration_tests/planner.rs index e0a620a3c8d..b9a06e34100 100644 --- a/nexus/reconfigurator/planning/tests/integration_tests/planner.rs +++ b/nexus/reconfigurator/planning/tests/integration_tests/planner.rs @@ -12,10 +12,6 @@ use iddqd::IdOrdMap; use nexus_reconfigurator_planning::blueprint_editor::ExternalNetworkingAllocator; use nexus_reconfigurator_simulation::BlueprintId; use nexus_reconfigurator_simulation::CollectionId; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::OmicronZoneType; -use nexus_sled_agent_shared::inventory::ZoneKind; use nexus_types::deployment::Blueprint; use nexus_types::deployment::BlueprintArtifactVersion; use nexus_types::deployment::BlueprintDatasetDisposition; @@ -74,6 +70,10 @@ use omicron_uuid_kinds::ZpoolUuid; use oxnet::Ipv6Net; use reconfigurator_cli::test_utils::ReconfiguratorCliTestState; use semver::Version; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types::inventory::OmicronZoneType; +use sled_agent_types::inventory::ZoneKind; use slog_error_chain::InlineErrorChain; use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/nexus/reconfigurator/simulation/Cargo.toml b/nexus/reconfigurator/simulation/Cargo.toml index 88becdb5484..21c9f98a85d 100644 --- a/nexus/reconfigurator/simulation/Cargo.toml +++ b/nexus/reconfigurator/simulation/Cargo.toml @@ -15,8 +15,8 @@ indexmap.workspace = true itertools.workspace = true nexus-inventory.workspace = true nexus-reconfigurator-planning.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true +sled-agent-types.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/nexus/reconfigurator/simulation/src/zone_images.rs b/nexus/reconfigurator/simulation/src/zone_images.rs index 15b263d6d3f..3255ba5d2c0 100644 --- a/nexus/reconfigurator/simulation/src/zone_images.rs +++ b/nexus/reconfigurator/simulation/src/zone_images.rs @@ -9,12 +9,12 @@ use std::collections::BTreeSet; use anyhow::bail; use camino::Utf8Path; use itertools::Itertools; -use nexus_sled_agent_shared::inventory::{ - ZoneArtifactInventory, ZoneKind, ZoneManifestBootInventory, -}; use omicron_common::{ api::external::TufRepoDescription, update::OmicronZoneManifestSource, }; +use sled_agent_types::inventory::{ + ZoneArtifactInventory, ZoneKind, ZoneManifestBootInventory, +}; use swrite::{SWrite, swrite}; use tufaceous_artifact::KnownArtifactKind; diff --git a/nexus/src/app/background/tasks/blueprint_execution.rs b/nexus/src/app/background/tasks/blueprint_execution.rs index 790047d2e56..be6c31a889c 100644 --- a/nexus/src/app/background/tasks/blueprint_execution.rs +++ b/nexus/src/app/background/tasks/blueprint_execution.rs @@ -234,7 +234,6 @@ mod test { use nexus_db_queries::authn; use nexus_db_queries::context::OpContext; use nexus_db_queries::db::DataStore; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_test_utils_macros::nexus_test; use nexus_types::deployment::execution::{ EventBuffer, EventReport, ExecutionComponent, ExecutionStepId, @@ -258,6 +257,7 @@ mod test { use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; use serde_json::json; + use sled_agent_types::inventory::OmicronZoneDataset; use std::collections::BTreeMap; use std::net::Ipv6Addr; use std::net::SocketAddr; diff --git a/nexus/src/app/background/tasks/sync_service_zone_nat.rs b/nexus/src/app/background/tasks/sync_service_zone_nat.rs index f325b75ad9f..3483d439c20 100644 --- a/nexus/src/app/background/tasks/sync_service_zone_nat.rs +++ b/nexus/src/app/background/tasks/sync_service_zone_nat.rs @@ -16,10 +16,10 @@ use nexus_db_lookup::LookupPath; use nexus_db_model::NatEntryValues; use nexus_db_queries::context::OpContext; use nexus_db_queries::db::DataStore; -use nexus_sled_agent_shared::inventory::OmicronZoneType; use nexus_types::inventory::Collection; use omicron_common::address::{MAX_PORT, MIN_PORT}; use serde_json::json; +use sled_agent_types::inventory::OmicronZoneType; use std::sync::Arc; use tokio::sync::watch; diff --git a/nexus/src/app/sled.rs b/nexus/src/app/sled.rs index 75e1bfdea76..aace6d30ef1 100644 --- a/nexus/src/app/sled.rs +++ b/nexus/src/app/sled.rs @@ -13,7 +13,6 @@ use nexus_db_lookup::lookup; use nexus_db_queries::authz; use nexus_db_queries::context::OpContext; use nexus_db_queries::db; -use nexus_sled_agent_shared::inventory::SledRole; use nexus_types::deployment::DiskFilter; use nexus_types::deployment::SledFilter; use nexus_types::external_api::views::PhysicalDiskPolicy; @@ -32,6 +31,7 @@ use omicron_uuid_kinds::PropolisUuid; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; use sled_agent_client::Client as SledAgentClient; +use sled_agent_types::inventory::SledRole; use std::net::SocketAddrV6; use std::sync::Arc; use uuid::Uuid; diff --git a/nexus/src/lib.rs b/nexus/src/lib.rs index cb47d7b9f8d..f3e65e5caee 100644 --- a/nexus/src/lib.rs +++ b/nexus/src/lib.rs @@ -342,7 +342,7 @@ impl nexus_test_interface::NexusServer for Server { >, internal_dns_zone_config: nexus_types::internal_api::params::DnsConfigParams, external_dns_zone_name: &str, - recovery_silo: nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig, + recovery_silo: sled_agent_types::rack_init::RecoverySiloConfig, certs: Vec, ) -> Self { // Perform the "handoff from RSS". diff --git a/nexus/test-interface/Cargo.toml b/nexus/test-interface/Cargo.toml index 20455777f9a..d522ec294a2 100644 --- a/nexus/test-interface/Cargo.toml +++ b/nexus/test-interface/Cargo.toml @@ -14,8 +14,8 @@ omicron-rpaths.workspace = true async-trait.workspace = true nexus-config.workspace = true nexus-db-queries.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true +sled-agent-types.workspace = true omicron-common.workspace = true # See omicron-rpaths for more about the "pq-sys" dependency. pq-sys = "*" diff --git a/nexus/test-interface/src/lib.rs b/nexus/test-interface/src/lib.rs index a9abdd16d16..2fa970218fb 100644 --- a/nexus/test-interface/src/lib.rs +++ b/nexus/test-interface/src/lib.rs @@ -78,7 +78,7 @@ pub trait NexusServer: Send + Sync + 'static { >, internal_dns_config: nexus_types::internal_api::params::DnsConfigParams, external_dns_zone_name: &str, - recovery_silo: nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig, + recovery_silo: sled_agent_types::rack_init::RecoverySiloConfig, tls_certificates: Vec< omicron_common::api::internal::nexus::Certificate, >, diff --git a/nexus/test-utils/Cargo.toml b/nexus/test-utils/Cargo.toml index e95fcd6b268..fc47798a24f 100644 --- a/nexus/test-utils/Cargo.toml +++ b/nexus/test-utils/Cargo.toml @@ -34,7 +34,6 @@ nexus-client.workspace = true nexus-config.workspace = true nexus-db-queries = { workspace = true, features = [ "testing" ] } nexus-lockstep-client.workspace = true -nexus-sled-agent-shared.workspace = true nexus-test-interface.workspace = true nexus-types.workspace = true omicron-cockroach-admin.workspace = true @@ -53,6 +52,7 @@ serde.workspace = true serde_json.workspace = true serde_urlencoded.workspace = true sled-agent-client.workspace = true +sled-agent-types.workspace = true slog.workspace = true slog-error-chain.workspace = true tokio.workspace = true diff --git a/nexus/test-utils/src/starter.rs b/nexus/test-utils/src/starter.rs index ebde32998c6..412959d3d63 100644 --- a/nexus/test-utils/src/starter.rs +++ b/nexus/test-utils/src/starter.rs @@ -30,11 +30,6 @@ use nexus_config::MgdConfig; use nexus_config::NUM_INITIAL_RESERVED_IP_ADDRESSES; use nexus_config::NexusConfig; use nexus_db_queries::db::pub_test_utils::crdb; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; -use nexus_sled_agent_shared::inventory::SledCpuFamily; -use nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig; use nexus_test_interface::InternalServer; use nexus_test_interface::NexusServer; use nexus_types::deployment::Blueprint; @@ -98,6 +93,11 @@ use oximeter_producer::Server as ProducerServer; use sled_agent_client::types::EarlyNetworkConfig; use sled_agent_client::types::EarlyNetworkConfigBody; use sled_agent_client::types::RackNetworkConfigV2; +use sled_agent_types::inventory::HostPhase2DesiredSlots; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::OmicronZoneDataset; +use sled_agent_types::inventory::SledCpuFamily; +use sled_agent_types::rack_init::RecoverySiloConfig; use slog::{Logger, debug, error, o}; use std::collections::BTreeMap; use std::collections::HashMap; diff --git a/nexus/tests/integration_tests/instances.rs b/nexus/tests/integration_tests/instances.rs index 31e37a4be10..d7e626c7bb0 100644 --- a/nexus/tests/integration_tests/instances.rs +++ b/nexus/tests/integration_tests/instances.rs @@ -1137,7 +1137,7 @@ async fn test_instance_migration_compatible_cpu_platforms( Some(nexus_address), Some(&camino::Utf8Path::new("/an/unused/update/directory")), omicron_sled_agent::sim::ZpoolConfig::None, - nexus_sled_agent_shared::inventory::SledCpuFamily::AmdTurin, + sled_agent_types::inventory::SledCpuFamily::AmdTurin, ); let new_sled_id = config.id; @@ -1326,7 +1326,7 @@ async fn test_instance_migration_incompatible_cpu_platforms( Some(nexus_address), Some(&camino::Utf8Path::new("/an/unused/update/directory")), omicron_sled_agent::sim::ZpoolConfig::None, - nexus_sled_agent_shared::inventory::SledCpuFamily::AmdTurin, + sled_agent_types::inventory::SledCpuFamily::AmdTurin, ); let turin_sled_id = config.id; @@ -1403,7 +1403,7 @@ async fn test_instance_migration_unknown_sled_type( Some(nexus_address), Some(&camino::Utf8Path::new("/an/unused/update/directory")), omicron_sled_agent::sim::ZpoolConfig::None, - nexus_sled_agent_shared::inventory::SledCpuFamily::Unknown, + sled_agent_types::inventory::SledCpuFamily::Unknown, ); let new_sled_id = config.id; @@ -6750,7 +6750,7 @@ async fn test_can_start_instance_with_cpu_platform( Some(nexus_address), Some(&camino::Utf8Path::new("/an/unused/update/directory")), omicron_sled_agent::sim::ZpoolConfig::None, - nexus_sled_agent_shared::inventory::SledCpuFamily::AmdTurin, + sled_agent_types::inventory::SledCpuFamily::AmdTurin, ); let new_sled_id = config.id; diff --git a/nexus/types/Cargo.toml b/nexus/types/Cargo.toml index 5e9b9382644..973f7d0993f 100644 --- a/nexus/types/Cargo.toml +++ b/nexus/types/Cargo.toml @@ -62,24 +62,21 @@ api_identity.workspace = true gateway-client.workspace = true gateway-types.workspace = true internal-dns-types.workspace = true -nexus-sled-agent-shared.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true omicron-workspace-hack.workspace = true semver.workspace = true +sled-agent-types-versions.workspace = true swrite.workspace = true tough.workspace = true -# Note: we're trying to avoid a dependency from nexus-types to sled-agent-types -# because the correct direction of dependency is unclear. If there are types -# common to both, put them in `omicron-common` or `nexus-sled-agent-shared`. [dev-dependencies] expectorate.workspace = true gateway-types = { workspace = true, features = ["testing"] } iddqd = { workspace = true, features = ["proptest"] } newtype-uuid = { workspace = true, features = ["proptest1"] } -nexus-sled-agent-shared = { workspace = true, features = ["testing"] } omicron-common = { workspace = true, features = ["testing"] } omicron-test-utils.workspace = true proptest.workspace = true +sled-agent-types-versions = { workspace = true, features = ["testing"] } test-strategy.workspace = true diff --git a/nexus/types/src/deployment.rs b/nexus/types/src/deployment.rs index 12360c88966..52a61e35623 100644 --- a/nexus/types/src/deployment.rs +++ b/nexus/types/src/deployment.rs @@ -28,12 +28,6 @@ use iddqd::IdOrdMap; use iddqd::id_ord_map::Entry; use iddqd::id_ord_map::RefMut; use iddqd::id_upcast; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredContents; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::address::Ipv6Subnet; use omicron_common::address::SLED_PREFIX; use omicron_common::api::external::ByteCount; @@ -57,6 +51,12 @@ use omicron_uuid_kinds::ZpoolUuid; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; +use sled_agent_types_versions::latest::inventory::HostPhase2DesiredContents; +use sled_agent_types_versions::latest::inventory::HostPhase2DesiredSlots; +use sled_agent_types_versions::latest::inventory::OmicronSledConfig; +use sled_agent_types_versions::latest::inventory::OmicronZoneConfig; +use sled_agent_types_versions::latest::inventory::OmicronZoneImageSource; +use sled_agent_types_versions::latest::inventory::ZoneKind; use slog::Key; use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/nexus/types/src/deployment/blueprint_diff.rs b/nexus/types/src/deployment/blueprint_diff.rs index f0e3348cfc4..b2966e18569 100644 --- a/nexus/types/src/deployment/blueprint_diff.rs +++ b/nexus/types/src/deployment/blueprint_diff.rs @@ -21,11 +21,11 @@ use super::{ zone_sort_key, }; use daft::{Diffable, Leaf}; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::api::external::ByteCount; use omicron_common::disk::{CompressionAlgorithm, DatasetName}; use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::{DatasetUuid, OmicronZoneUuid, PhysicalDiskUuid}; +use sled_agent_types_versions::latest::inventory::ZoneKind; use std::collections::{BTreeMap, BTreeSet}; use std::fmt::{self, Write as _}; diff --git a/nexus/types/src/deployment/execution/utils.rs b/nexus/types/src/deployment/execution/utils.rs index f9e21bc608d..fd6bb3f87b3 100644 --- a/nexus/types/src/deployment/execution/utils.rs +++ b/nexus/types/src/deployment/execution/utils.rs @@ -5,12 +5,12 @@ use std::net::{IpAddr, SocketAddrV6}; use iddqd::{IdOrdItem, id_upcast}; -use nexus_sled_agent_shared::inventory::SledRole; use omicron_common::{ address::{Ipv6Subnet, SLED_PREFIX}, api::external::Generation, }; use omicron_uuid_kinds::SledUuid; +use sled_agent_types_versions::latest::inventory::SledRole; use crate::{ deployment::{ diff --git a/nexus/types/src/deployment/planning_input.rs b/nexus/types/src/deployment/planning_input.rs index d7232543070..3b38e748b6b 100644 --- a/nexus/types/src/deployment/planning_input.rs +++ b/nexus/types/src/deployment/planning_input.rs @@ -24,7 +24,6 @@ use chrono::Utc; use clap::ValueEnum; use daft::Diffable; use ipnetwork::IpNetwork; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::address::IpRange; use omicron_common::address::Ipv4Range; use omicron_common::address::Ipv6Range; @@ -43,6 +42,7 @@ use omicron_uuid_kinds::ZpoolUuid; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; +use sled_agent_types_versions::latest::inventory::ZoneKind; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::collections::btree_map::Entry; diff --git a/nexus/types/src/deployment/planning_report.rs b/nexus/types/src/deployment/planning_report.rs index 7c7992a27b8..2d260f857ed 100644 --- a/nexus/types/src/deployment/planning_report.rs +++ b/nexus/types/src/deployment/planning_report.rs @@ -17,7 +17,6 @@ use daft::Diffable; use iddqd::IdOrdItem; use iddqd::id_upcast; use indent_write::fmt::IndentWriter; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::api::external::Generation; use omicron_common::disk::M2Slot; use omicron_common::policy::BOUNDARY_NTP_REDUNDANCY; @@ -31,6 +30,7 @@ use omicron_uuid_kinds::ZpoolUuid; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; +use sled_agent_types_versions::latest::inventory::ZoneKind; use slog_error_chain::InlineErrorChain; use std::collections::BTreeMap; use std::collections::BTreeSet; diff --git a/nexus/types/src/deployment/zone_type.rs b/nexus/types/src/deployment/zone_type.rs index 18a81ec7e5c..3dcc80919ff 100644 --- a/nexus/types/src/deployment/zone_type.rs +++ b/nexus/types/src/deployment/zone_type.rs @@ -10,15 +10,15 @@ use super::OmicronZoneExternalIp; use daft::Diffable; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; -use nexus_sled_agent_shared::inventory::OmicronZoneType; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::api::internal::shared::DatasetKind; use omicron_common::api::internal::shared::NetworkInterface; use omicron_common::disk::DatasetName; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; +use sled_agent_types_versions::latest::inventory::OmicronZoneDataset; +use sled_agent_types_versions::latest::inventory::OmicronZoneType; +use sled_agent_types_versions::latest::inventory::ZoneKind; use std::net::Ipv6Addr; #[derive( @@ -343,12 +343,12 @@ pub mod blueprint_zone_type { use crate::deployment::OmicronZoneExternalFloatingIp; use crate::deployment::OmicronZoneExternalSnatIp; use daft::Diffable; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use omicron_common::api::external::Generation; use omicron_common::api::internal::shared::NetworkInterface; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; + use sled_agent_types_versions::latest::inventory::OmicronZoneDataset; use std::net::IpAddr; use std::net::Ipv6Addr; use std::net::SocketAddrV6; diff --git a/nexus/types/src/internal_api/params.rs b/nexus/types/src/internal_api/params.rs index 7c4d4296677..f21f46027de 100644 --- a/nexus/types/src/internal_api/params.rs +++ b/nexus/types/src/internal_api/params.rs @@ -8,8 +8,6 @@ use crate::deployment::Blueprint; use crate::external_api::params::PhysicalDiskKind; use crate::external_api::shared::Baseboard; use crate::external_api::shared::IpRange; -use nexus_sled_agent_shared::inventory::{SledCpuFamily, SledRole}; -use nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig; use omicron_common::api::external::ByteCount; use omicron_common::api::external::Generation; use omicron_common::api::external::MacAddr; @@ -25,6 +23,8 @@ use omicron_uuid_kinds::SledUuid; use omicron_uuid_kinds::ZpoolUuid; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +use sled_agent_types_versions::latest::inventory::{SledCpuFamily, SledRole}; +use sled_agent_types_versions::latest::rack_init::RecoverySiloConfig; use std::fmt; use std::net::IpAddr; use std::net::SocketAddr; diff --git a/nexus/types/src/internal_api/views.rs b/nexus/types/src/internal_api/views.rs index 165e3b0f661..e9ed52ad728 100644 --- a/nexus/types/src/internal_api/views.rs +++ b/nexus/types/src/internal_api/views.rs @@ -18,11 +18,6 @@ use gateway_types::rot::RotSlot; use iddqd::IdOrdItem; use iddqd::IdOrdMap; use iddqd::id_upcast; -use nexus_sled_agent_shared::inventory::BootPartitionContents; -use nexus_sled_agent_shared::inventory::BootPartitionDetails; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; -use nexus_sled_agent_shared::inventory::OmicronZoneType; use omicron_common::api::external::MacAddr; use omicron_common::api::external::ObjectStream; use omicron_common::api::external::TufArtifactMeta; @@ -36,6 +31,11 @@ use schemars::JsonSchema; use semver::Version; use serde::Deserialize; use serde::Serialize; +use sled_agent_types_versions::latest::inventory::BootPartitionContents; +use sled_agent_types_versions::latest::inventory::BootPartitionDetails; +use sled_agent_types_versions::latest::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types_versions::latest::inventory::OmicronZoneImageSource; +use sled_agent_types_versions::latest::inventory::OmicronZoneType; use std::collections::BTreeMap; use std::collections::VecDeque; use std::fmt::Display; diff --git a/nexus/types/src/inventory.rs b/nexus/types/src/inventory.rs index cf03a0bec94..b95527e2d06 100644 --- a/nexus/types/src/inventory.rs +++ b/nexus/types/src/inventory.rs @@ -22,17 +22,6 @@ pub use gateway_types::rot::RotSlot; use iddqd::IdOrdItem; use iddqd::IdOrdMap; use iddqd::id_upcast; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::InventoryDisk; -use nexus_sled_agent_shared::inventory::InventoryZpool; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneConfig; -use nexus_sled_agent_shared::inventory::SledCpuFamily; -use nexus_sled_agent_shared::inventory::SledRole; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; use omicron_common::api::external::ByteCount; pub use omicron_common::api::internal::shared::NetworkInterface; pub use omicron_common::api::internal::shared::NetworkInterfaceKind; @@ -47,6 +36,17 @@ use omicron_uuid_kinds::ZpoolUuid; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::serde_as; +use sled_agent_types_versions::latest::inventory::ConfigReconcilerInventory; +use sled_agent_types_versions::latest::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types_versions::latest::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types_versions::latest::inventory::InventoryDataset; +use sled_agent_types_versions::latest::inventory::InventoryDisk; +use sled_agent_types_versions::latest::inventory::InventoryZpool; +use sled_agent_types_versions::latest::inventory::OmicronSledConfig; +use sled_agent_types_versions::latest::inventory::OmicronZoneConfig; +use sled_agent_types_versions::latest::inventory::SledCpuFamily; +use sled_agent_types_versions::latest::inventory::SledRole; +use sled_agent_types_versions::latest::inventory::ZoneImageResolverInventory; use std::collections::BTreeMap; use std::collections::BTreeSet; use std::net::SocketAddrV6; diff --git a/nexus/types/src/inventory/display.rs b/nexus/types/src/inventory/display.rs index 92a58f61b37..d220d876fd8 100644 --- a/nexus/types/src/inventory/display.rs +++ b/nexus/types/src/inventory/display.rs @@ -16,17 +16,17 @@ use gateway_types::component::SpType; use iddqd::IdOrdMap; use indent_write::fmt::IndentWriter; use itertools::Itertools; -use nexus_sled_agent_shared::inventory::{ +use omicron_common::disk::M2Slot; +use omicron_uuid_kinds::{ + DatasetUuid, OmicronZoneUuid, PhysicalDiskUuid, ZpoolUuid, +}; +use sled_agent_types_versions::latest::inventory::{ BootImageHeader, BootPartitionContents, BootPartitionDetails, ConfigReconcilerInventory, ConfigReconcilerInventoryResult, ConfigReconcilerInventoryStatus, HostPhase2DesiredContents, OmicronSledConfig, OmicronZoneImageSource, OrphanedDataset, RemoveMupdateOverrideBootSuccessInventory, }; -use omicron_common::disk::M2Slot; -use omicron_uuid_kinds::{ - DatasetUuid, OmicronZoneUuid, PhysicalDiskUuid, ZpoolUuid, -}; use std::collections::HashMap; use strum::IntoEnumIterator; use tabled::Tabled; diff --git a/openapi/nexus-lockstep.json b/openapi/nexus-lockstep.json index 1b6ea918840..2f9724cdb93 100644 --- a/openapi/nexus-lockstep.json +++ b/openapi/nexus-lockstep.json @@ -7976,6 +7976,7 @@ ] }, "RecoverySiloConfig": { + "description": "Configuration for the recovery silo created during rack setup.", "type": "object", "properties": { "silo_name": { diff --git a/sled-agent/Cargo.toml b/sled-agent/Cargo.toml index dbc21297201..ebe68d481d9 100644 --- a/sled-agent/Cargo.toml +++ b/sled-agent/Cargo.toml @@ -58,7 +58,6 @@ mg-admin-client.workspace = true nexus-client.workspace = true nexus-config.workspace = true nexus-lockstep-client.workspace = true -nexus-sled-agent-shared.workspace = true nexus-types.workspace = true ntp-admin-client.workspace = true omicron-common.workspace = true @@ -89,6 +88,7 @@ sled-agent-api.workspace = true sled-agent-client.workspace = true sled-agent-config-reconciler.workspace = true sled-agent-types.workspace = true +sled-agent-types-versions.workspace = true sled-agent-zone-images.workspace = true sled-diagnostics.workspace = true sled-hardware.workspace = true diff --git a/sled-agent/api/Cargo.toml b/sled-agent/api/Cargo.toml index fa276cc2367..9725368a656 100644 --- a/sled-agent/api/Cargo.toml +++ b/sled-agent/api/Cargo.toml @@ -14,7 +14,7 @@ dropshot.workspace = true dropshot-api-manager-types.workspace = true http.workspace = true iddqd.workspace = true -nexus-sled-agent-shared.workspace = true +sled-agent-types-versions.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/sled-agent/api/src/lib.rs b/sled-agent/api/src/lib.rs index caacc8b89ab..615dcb86331 100644 --- a/sled-agent/api/src/lib.rs +++ b/sled-agent/api/src/lib.rs @@ -2,8 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use std::collections::{BTreeMap, BTreeSet}; -use std::time::Duration; +use std::collections::BTreeMap; use camino::Utf8PathBuf; use dropshot::{ @@ -13,55 +12,15 @@ use dropshot::{ StreamingBody, TypedBody, }; use dropshot_api_manager_types::api_versions; -use nexus_sled_agent_shared::inventory::{ - Inventory, OmicronSledConfig, SledRole, -}; -use omicron_common::{ - api::external::ByteCount, - api::external::Generation, - api::internal::{ - nexus::{DiskRuntimeState, SledVmmState}, - shared::{ - ExternalIpGatewayMap, ResolvedVpcRouteSet, ResolvedVpcRouteState, - SledIdentifiers, SwitchPorts, VirtualNetworkInterfaceHost, - }, - }, - disk::DiskVariant, - ledger::Ledgerable, -}; -use omicron_uuid_kinds::{ - DatasetUuid, ExternalZpoolUuid, PropolisUuid, SupportBundleUuid, ZpoolUuid, -}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use sled_agent_types::inventory::v9; -use sled_agent_types::inventory::v10; -use sled_agent_types::probes; -use sled_agent_types::{ - bootstore::BootstoreStatus, - disk::DiskEnsureBody, - early_networking::EarlyNetworkConfig, - firewall_rules::VpcFirewallRulesEnsureBody, - instance::{ - InstanceEnsureBody, InstanceExternalIpBody, InstanceMulticastBody, - VmmPutStateBody, VmmPutStateResponse, VmmUnregisterResponse, - }, - sled::AddSledRequest, - zone_bundle::{ - BundleUtilization, CleanupContext, CleanupCount, PriorityOrder, - ZoneBundleId, ZoneBundleMetadata, +use omicron_common::api::internal::{ + nexus::{DiskRuntimeState, SledVmmState}, + shared::{ + ExternalIpGatewayMap, ResolvedVpcRouteSet, ResolvedVpcRouteState, + SledIdentifiers, SwitchPorts, VirtualNetworkInterfaceHost, }, }; +use sled_agent_types_versions::{latest, v1, v4, v6, v7, v9, v10}; use sled_diagnostics::SledDiagnosticsQueryOutput; -use tufaceous_artifact::ArtifactHash; -use uuid::Uuid; - -/// Copies of data types that changed between v3 and v4. -mod v3; -/// Copies of data types that changed between v6 and v7. -mod v6; -/// Copies of data types that changed between v8 and v9. -mod v8; api_versions!([ // WHEN CHANGING THE API (part 1 of 2): @@ -122,8 +81,11 @@ pub trait SledAgentApi { }] async fn zone_bundle_list_all( rqctx: RequestContext, - query: Query, - ) -> Result>, HttpError>; + query: Query, + ) -> Result< + HttpResponseOk>, + HttpError, + >; /// List the zone bundles that are available for a running zone. #[endpoint { @@ -132,8 +94,11 @@ pub trait SledAgentApi { }] async fn zone_bundle_list( rqctx: RequestContext, - params: Path, - ) -> Result>, HttpError>; + params: Path, + ) -> Result< + HttpResponseOk>, + HttpError, + >; /// Fetch the binary content of a single zone bundle. #[endpoint { @@ -142,7 +107,7 @@ pub trait SledAgentApi { }] async fn zone_bundle_get( rqctx: RequestContext, - params: Path, + params: Path, ) -> Result>, HttpError>; /// Delete a zone bundle. @@ -152,7 +117,7 @@ pub trait SledAgentApi { }] async fn zone_bundle_delete( rqctx: RequestContext, - params: Path, + params: Path, ) -> Result; /// Return utilization information about all zone bundles. @@ -163,7 +128,9 @@ pub trait SledAgentApi { async fn zone_bundle_utilization( rqctx: RequestContext, ) -> Result< - HttpResponseOk>, + HttpResponseOk< + BTreeMap, + >, HttpError, >; @@ -174,7 +141,7 @@ pub trait SledAgentApi { }] async fn zone_bundle_cleanup_context( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// Update context used by the zone-bundle cleanup task. #[endpoint { @@ -183,7 +150,7 @@ pub trait SledAgentApi { }] async fn zone_bundle_cleanup_context_update( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; /// Trigger a zone bundle cleanup. @@ -193,7 +160,15 @@ pub trait SledAgentApi { }] async fn zone_bundle_cleanup( rqctx: RequestContext, - ) -> Result>, HttpError>; + ) -> Result< + HttpResponseOk< + std::collections::BTreeMap< + Utf8PathBuf, + latest::zone_bundle::CleanupCount, + >, + >, + HttpError, + >; /// List the zones that are currently managed by the sled agent. #[endpoint { @@ -211,8 +186,11 @@ pub trait SledAgentApi { }] async fn support_bundle_list( rqctx: RequestContext, - path_params: Path, - ) -> Result>, HttpError>; + path_params: Path, + ) -> Result< + HttpResponseOk>, + HttpError, + >; /// Starts creation of a support bundle within a particular dataset /// @@ -231,8 +209,11 @@ pub trait SledAgentApi { }] async fn support_bundle_start_creation( rqctx: RequestContext, - path_params: Path, - ) -> Result, HttpError>; + path_params: Path, + ) -> Result< + HttpResponseCreated, + HttpError, + >; /// Transfers a chunk of a support bundle within a particular dataset #[endpoint { @@ -242,10 +223,15 @@ pub trait SledAgentApi { }] async fn support_bundle_transfer( rqctx: RequestContext, - path_params: Path, - query_params: Query, + path_params: Path, + query_params: Query< + latest::support_bundle::SupportBundleTransferQueryParams, + >, body: StreamingBody, - ) -> Result, HttpError>; + ) -> Result< + HttpResponseCreated, + HttpError, + >; /// Finalizes the creation of a support bundle /// @@ -257,9 +243,14 @@ pub trait SledAgentApi { }] async fn support_bundle_finalize( rqctx: RequestContext, - path_params: Path, - query_params: Query, - ) -> Result, HttpError>; + path_params: Path, + query_params: Query< + latest::support_bundle::SupportBundleFinalizeQueryParams, + >, + ) -> Result< + HttpResponseCreated, + HttpError, + >; /// Fetch a support bundle from a particular dataset #[endpoint { @@ -268,8 +259,8 @@ pub trait SledAgentApi { }] async fn support_bundle_download( rqctx: RequestContext, - headers: Header, - path_params: Path, + headers: Header, + path_params: Path, ) -> Result, HttpError>; /// Fetch a file within a support bundle from a particular dataset @@ -279,8 +270,8 @@ pub trait SledAgentApi { }] async fn support_bundle_download_file( rqctx: RequestContext, - headers: Header, - path_params: Path, + headers: Header, + path_params: Path, ) -> Result, HttpError>; /// Fetch the index (list of files within a support bundle) @@ -290,8 +281,8 @@ pub trait SledAgentApi { }] async fn support_bundle_index( rqctx: RequestContext, - headers: Header, - path_params: Path, + headers: Header, + path_params: Path, ) -> Result, HttpError>; /// Fetch metadata about a support bundle from a particular dataset @@ -301,8 +292,8 @@ pub trait SledAgentApi { }] async fn support_bundle_head( rqctx: RequestContext, - headers: Header, - path_params: Path, + headers: Header, + path_params: Path, ) -> Result, HttpError>; /// Fetch metadata about a file within a support bundle from a particular dataset @@ -312,8 +303,8 @@ pub trait SledAgentApi { }] async fn support_bundle_head_file( rqctx: RequestContext, - headers: Header, - path_params: Path, + headers: Header, + path_params: Path, ) -> Result, HttpError>; /// Fetch metadata about the list of files within a support bundle @@ -323,8 +314,8 @@ pub trait SledAgentApi { }] async fn support_bundle_head_index( rqctx: RequestContext, - headers: Header, - path_params: Path, + headers: Header, + path_params: Path, ) -> Result, HttpError>; /// Delete a support bundle from a particular dataset @@ -334,7 +325,7 @@ pub trait SledAgentApi { }] async fn support_bundle_delete( rqctx: RequestContext, - path_params: Path, + path_params: Path, ) -> Result; #[endpoint { @@ -344,7 +335,7 @@ pub trait SledAgentApi { }] async fn omicron_config_put( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; #[endpoint { @@ -354,26 +345,28 @@ pub trait SledAgentApi { versions = VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES..VERSION_ADD_DUAL_STACK_EXTERNAL_IP_CONFIG, }] - async fn v10_omicron_config_put( + async fn omicron_config_put_v10( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result { - let body = body.try_map(OmicronSledConfig::try_from)?; + let body = + body.try_map(latest::inventory::OmicronSledConfig::try_from)?; Self::omicron_config_put(rqctx, body).await } #[endpoint { + operation_id = "omicron_config_put", method = PUT, path = "/omicron-config", versions = VERSION_ADD_NEXUS_LOCKSTEP_PORT_TO_INVENTORY..VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES, }] - async fn v9_omicron_config_put( + async fn omicron_config_put_v4( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result { - let body = body.try_map(v10::OmicronSledConfig::try_from)?; - Self::v10_omicron_config_put(rqctx, body).await + let body = body.try_map(v10::inventory::OmicronSledConfig::try_from)?; + Self::omicron_config_put_v10(rqctx, body).await } #[endpoint { @@ -382,21 +375,22 @@ pub trait SledAgentApi { path = "/omicron-config", versions = ..VERSION_ADD_NEXUS_LOCKSTEP_PORT_TO_INVENTORY, }] - async fn v3_omicron_config_put( + async fn omicron_config_put_v1( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result { - Self::v9_omicron_config_put(rqctx, body.map(Into::into)).await + Self::omicron_config_put_v4(rqctx, body.map(Into::into)).await } #[endpoint { + operation_id = "sled_role_get", method = GET, path = "/sled-role", versions = ..VERSION_REMOVE_SLED_ROLE, }] - async fn sled_role_get( + async fn sled_role_get_v1( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; #[endpoint { operation_id = "vmm_register", @@ -406,25 +400,24 @@ pub trait SledAgentApi { }] async fn vmm_register( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result, HttpError>; #[endpoint { + operation_id = "vmm_register", method = PUT, path = "/vmms/{propolis_id}", - operation_id = "vmm_register", versions = VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES..VERSION_ADD_DUAL_STACK_EXTERNAL_IP_CONFIG }] - async fn v10_vmm_register( + async fn vmm_register_v10( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result, HttpError> { - let body = body.try_map( - sled_agent_types::instance::InstanceEnsureBody::try_from, - )?; + let body = + body.try_map(latest::instance::InstanceEnsureBody::try_from)?; Self::vmm_register(rqctx, path_params, body).await } @@ -435,13 +428,13 @@ pub trait SledAgentApi { versions = VERSION_DELEGATE_ZVOL_TO_PROPOLIS..VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES }] - async fn v9_vmm_register( + async fn vmm_register_v9( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result, HttpError> { - let body = body.try_map(v10::InstanceEnsureBody::try_from)?; - Self::v10_vmm_register(rqctx, path_params, body).await + let body = body.try_map(v10::instance::InstanceEnsureBody::try_from)?; + Self::vmm_register_v10(rqctx, path_params, body).await } #[endpoint { @@ -450,26 +443,26 @@ pub trait SledAgentApi { path = "/vmms/{propolis_id}", versions = VERSION_MULTICAST_SUPPORT..VERSION_DELEGATE_ZVOL_TO_PROPOLIS }] - async fn v8_vmm_register( + async fn vmm_register_v7( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result, HttpError> { - Self::v9_vmm_register(rqctx, path_params, body.map(Into::into)).await + Self::vmm_register_v9(rqctx, path_params, body.map(Into::into)).await } #[endpoint { + operation_id = "vmm_register", method = PUT, path = "/vmms/{propolis_id}", - operation_id = "vmm_register", versions = ..VERSION_MULTICAST_SUPPORT }] - async fn v6_vmm_register( + async fn vmm_register_v1( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result, HttpError> { - Self::v8_vmm_register(rqctx, path_params, body.map(Into::into)).await + Self::vmm_register_v7(rqctx, path_params, body.map(Into::into)).await } #[endpoint { @@ -478,8 +471,11 @@ pub trait SledAgentApi { }] async fn vmm_unregister( rqctx: RequestContext, - path_params: Path, - ) -> Result, HttpError>; + path_params: Path, + ) -> Result< + HttpResponseOk, + HttpError, + >; #[endpoint { method = PUT, @@ -487,9 +483,9 @@ pub trait SledAgentApi { }] async fn vmm_put_state( rqctx: RequestContext, - path_params: Path, - body: TypedBody, - ) -> Result, HttpError>; + path_params: Path, + body: TypedBody, + ) -> Result, HttpError>; #[endpoint { method = GET, @@ -497,7 +493,7 @@ pub trait SledAgentApi { }] async fn vmm_get_state( rqctx: RequestContext, - path_params: Path, + path_params: Path, ) -> Result, HttpError>; #[endpoint { @@ -506,8 +502,8 @@ pub trait SledAgentApi { }] async fn vmm_put_external_ip( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result; #[endpoint { @@ -516,8 +512,8 @@ pub trait SledAgentApi { }] async fn vmm_delete_external_ip( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result; #[endpoint { @@ -527,8 +523,8 @@ pub trait SledAgentApi { }] async fn vmm_join_multicast_group( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result; #[endpoint { @@ -538,8 +534,8 @@ pub trait SledAgentApi { }] async fn vmm_leave_multicast_group( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result; #[endpoint { @@ -548,8 +544,8 @@ pub trait SledAgentApi { }] async fn disk_put( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result, HttpError>; #[endpoint { @@ -558,7 +554,7 @@ pub trait SledAgentApi { }] async fn artifact_config_get( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; #[endpoint { method = PUT, @@ -566,7 +562,7 @@ pub trait SledAgentApi { }] async fn artifact_config_put( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; #[endpoint { @@ -575,7 +571,7 @@ pub trait SledAgentApi { }] async fn artifact_list( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; #[endpoint { method = POST, @@ -583,10 +579,13 @@ pub trait SledAgentApi { }] async fn artifact_copy_from_depot( rqctx: RequestContext, - path_params: Path, - query_params: Query, - body: TypedBody, - ) -> Result, HttpError>; + path_params: Path, + query_params: Query, + body: TypedBody, + ) -> Result< + HttpResponseAccepted, + HttpError, + >; #[endpoint { method = PUT, @@ -595,10 +594,10 @@ pub trait SledAgentApi { }] async fn artifact_put( rqctx: RequestContext, - path_params: Path, - query_params: Query, + path_params: Path, + query_params: Query, body: StreamingBody, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// Take a snapshot of a disk that is attached to an instance #[endpoint { @@ -607,9 +606,14 @@ pub trait SledAgentApi { }] async fn vmm_issue_disk_snapshot_request( rqctx: RequestContext, - path_params: Path, - body: TypedBody, - ) -> Result, HttpError>; + path_params: Path< + latest::instance::VmmIssueDiskSnapshotRequestPathParam, + >, + body: TypedBody, + ) -> Result< + HttpResponseOk, + HttpError, + >; #[endpoint { method = PUT, @@ -618,8 +622,8 @@ pub trait SledAgentApi { }] async fn vpc_firewall_rules_put( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result; #[endpoint { @@ -628,12 +632,14 @@ pub trait SledAgentApi { path = "/vpc/{vpc_id}/firewall/rules", versions = ..VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES, }] - async fn v9_vpc_firewall_rules_put( + async fn vpc_firewall_rules_put_v1( rqctx: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result { - let body = body.try_map(VpcFirewallRulesEnsureBody::try_from)?; + let body = body.try_map( + latest::firewall_rules::VpcFirewallRulesEnsureBody::try_from, + )?; Self::vpc_firewall_rules_put(rqctx, path_params, body).await } @@ -689,7 +695,10 @@ pub trait SledAgentApi { }] async fn read_network_bootstore_config_cache( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result< + HttpResponseOk, + HttpError, + >; #[endpoint { method = PUT, @@ -697,7 +706,7 @@ pub trait SledAgentApi { }] async fn write_network_bootstore_config( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; /// Add a sled to a rack that was already initialized via RSS @@ -707,7 +716,7 @@ pub trait SledAgentApi { }] async fn sled_add( rqctx: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; /// Fetch basic information about this sled @@ -718,7 +727,7 @@ pub trait SledAgentApi { }] async fn inventory( rqctx: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// Fetch basic information about this sled #[endpoint { @@ -728,9 +737,9 @@ pub trait SledAgentApi { versions = VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES..VERSION_ADD_DUAL_STACK_EXTERNAL_IP_CONFIG, }] - async fn v10_inventory( + async fn inventory_v10( rqctx: RequestContext, - ) -> Result, HttpError> { + ) -> Result, HttpError> { let HttpResponseOk(inventory) = Self::inventory(rqctx).await?; inventory.try_into().map_err(HttpError::from).map(HttpResponseOk) } @@ -743,10 +752,10 @@ pub trait SledAgentApi { versions = VERSION_ADD_NEXUS_LOCKSTEP_PORT_TO_INVENTORY..VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES, }] - async fn v9_inventory( + async fn inventory_v4( rqctx: RequestContext, - ) -> Result, HttpError> { - let HttpResponseOk(inventory) = Self::v10_inventory(rqctx).await?; + ) -> Result, HttpError> { + let HttpResponseOk(inventory) = Self::inventory_v10(rqctx).await?; inventory.try_into().map_err(HttpError::from).map(HttpResponseOk) } @@ -757,12 +766,12 @@ pub trait SledAgentApi { path = "/inventory", versions = ..VERSION_ADD_NEXUS_LOCKSTEP_PORT_TO_INVENTORY, }] - async fn v3_inventory( + async fn inventory_v1( rqctx: RequestContext, - ) -> Result, HttpError> { - Self::v9_inventory(rqctx) - .await - .map(|HttpResponseOk(inv)| HttpResponseOk(v3::Inventory::from(inv))) + ) -> Result, HttpError> { + Self::inventory_v4(rqctx).await.map(|HttpResponseOk(inv)| { + HttpResponseOk(v1::inventory::Inventory::from(inv)) + }) } /// Fetch sled identifiers @@ -781,7 +790,7 @@ pub trait SledAgentApi { }] async fn bootstore_status( request_context: RequestContext, - ) -> Result, HttpError>; + ) -> Result, HttpError>; /// Get the current versions of VPC routing rules. #[endpoint { @@ -909,25 +918,34 @@ pub trait SledAgentApi { }] async fn support_logs_download( request_context: RequestContext, - path_params: Path, - query_params: Query, + path_params: Path< + latest::diagnostics::SledDiagnosticsLogsDownloadPathParam, + >, + query_params: Query< + latest::diagnostics::SledDiagnosticsLogsDownloadQueryParam, + >, ) -> Result, HttpError>; /// This endpoint reports the status of the `destroy_orphaned_datasets` /// chicken switch. It will be removed with omicron#6177. #[endpoint { + operation_id = "chicken_switch_destroy_orphaned_datasets_get", method = GET, path = "/chicken-switch/destroy-orphaned-datasets", versions = ..VERSION_REMOVE_DESTROY_ORPHANED_DATASETS_CHICKEN_SWITCH, }] - async fn chicken_switch_destroy_orphaned_datasets_get( + async fn chicken_switch_destroy_orphaned_datasets_get_v1( request_context: RequestContext, - ) -> Result, HttpError>; + ) -> Result< + HttpResponseOk, + HttpError, + >; /// This endpoint sets the `destroy_orphaned_datasets` chicken switch /// (allowing sled-agent to delete datasets it believes are orphaned). It /// will be removed with omicron#6177. #[endpoint { + operation_id = "chicken_switch_destroy_orphaned_datasets_put", method = PUT, path = "/chicken-switch/destroy-orphaned-datasets", // This should have been removed in @@ -935,9 +953,9 @@ pub trait SledAgentApi { // overlooked. This removes it as of the next version instead. versions = ..VERSION_ADD_SWITCH_ZONE_OPERATOR_POLICY, }] - async fn chicken_switch_destroy_orphaned_datasets_put( + async fn chicken_switch_destroy_orphaned_datasets_put_v1( request_context: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; /// A debugging endpoint only used by `omdb` that allows us to test @@ -950,7 +968,10 @@ pub trait SledAgentApi { }] async fn debug_operator_switch_zone_policy_get( request_context: RequestContext, - ) -> Result, HttpError>; + ) -> Result< + HttpResponseOk, + HttpError, + >; /// A debugging endpoint only used by `omdb` that allows us to test /// restarting the switch zone without restarting sled-agent. See @@ -967,7 +988,7 @@ pub trait SledAgentApi { }] async fn debug_operator_switch_zone_policy_put( request_context: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; /// Update the entire set of probe zones on this sled. @@ -982,7 +1003,7 @@ pub trait SledAgentApi { }] async fn probes_put( request_context: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result; /// Update the entire set of probe zones on this sled. @@ -991,16 +1012,17 @@ pub trait SledAgentApi { /// similar to instances, in that they have an OPTE port on a VPC subnet and /// external addresses, but no actual VM. #[endpoint { + operation_id = "probes_put", method = PUT, path = "/probes", versions = VERSION_ADD_PROBE_PUT_ENDPOINT..VERSION_ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES, }] - async fn v9_probes_put( + async fn probes_put_v6( request_context: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result { - let body = body.try_map(TryInto::try_into)?; + let body = body.try_map(latest::probes::ProbeSet::try_from)?; Self::probes_put(request_context, body).await } @@ -1012,8 +1034,8 @@ pub trait SledAgentApi { }] async fn local_storage_dataset_ensure( request_context: RequestContext, - path_params: Path, - body: TypedBody, + path_params: Path, + body: TypedBody, ) -> Result; /// Delete a local storage dataset @@ -1024,266 +1046,6 @@ pub trait SledAgentApi { }] async fn local_storage_dataset_delete( request_context: RequestContext, - path_params: Path, + path_params: Path, ) -> Result; } - -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct ChickenSwitchDestroyOrphanedDatasets { - /// If true, sled-agent will attempt to destroy durable ZFS datasets that it - /// believes were associated with now-expunged Omicron zones. - pub destroy_orphans: bool, -} - -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct ZoneBundleFilter { - /// An optional substring used to filter zone bundles. - pub filter: Option, -} - -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct ZonePathParam { - /// The name of the zone. - pub zone_name: String, -} - -/// Parameters used to update the zone bundle cleanup context. -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct CleanupContextUpdate { - /// The new period on which automatic cleanups are run. - pub period: Option, - /// The priority ordering for preserving old zone bundles. - pub priority: Option, - /// The new limit on the underlying dataset quota allowed for bundles. - pub storage_limit: Option, -} - -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] -pub struct Zpool { - pub id: ZpoolUuid, - pub disk_type: DiskType, -} - -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] -pub enum DiskType { - U2, - M2, -} - -impl From for DiskType { - fn from(v: DiskVariant) -> Self { - match v { - DiskVariant::U2 => Self::U2, - DiskVariant::M2 => Self::M2, - } - } -} - -/// Path parameters for Instance requests (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct VmmPathParam { - pub propolis_id: PropolisUuid, -} - -/// Path parameters for Support Bundle requests (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct SupportBundleListPathParam { - /// The zpool on which this support bundle was provisioned - pub zpool_id: ZpoolUuid, - - /// The dataset on which this support bundle was provisioned - pub dataset_id: DatasetUuid, -} - -/// Path parameters for Support Bundle requests (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct SupportBundlePathParam { - /// The zpool on which this support bundle was provisioned - pub zpool_id: ZpoolUuid, - - /// The dataset on which this support bundle was provisioned - pub dataset_id: DatasetUuid, - - /// The ID of the support bundle itself - pub support_bundle_id: SupportBundleUuid, -} - -/// Path parameters for Support Bundle requests (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct SupportBundleFilePathParam { - #[serde(flatten)] - pub parent: SupportBundlePathParam, - - /// The path of the file within the support bundle to query - pub file: String, -} - -/// Metadata about a support bundle transfer -#[derive(Deserialize, Serialize, JsonSchema)] -pub struct SupportBundleTransferQueryParams { - pub offset: u64, -} - -/// Metadata about a support bundle -#[derive(Deserialize, Serialize, JsonSchema)] -pub struct SupportBundleFinalizeQueryParams { - pub hash: ArtifactHash, -} - -#[derive(Deserialize, Serialize, JsonSchema)] -pub struct SupportBundleGetHeaders { - range: String, -} - -#[derive(Deserialize, Debug, Serialize, JsonSchema, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum SupportBundleState { - Complete, - Incomplete, -} - -/// Metadata about a support bundle -#[derive(Debug, Deserialize, Serialize, JsonSchema)] -pub struct SupportBundleMetadata { - pub support_bundle_id: SupportBundleUuid, - pub state: SupportBundleState, -} - -/// Range request headers -#[derive(Debug, Deserialize, Serialize, JsonSchema)] -pub struct RangeRequestHeaders { - /// A request to access a portion of the resource, such as `bytes=0-499` - /// - /// See: - pub range: Option, -} - -/// Path parameters for sled-diagnostics log requests used by support bundles -/// (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct SledDiagnosticsLogsDownloadPathParm { - /// The zone for which one would like to collect logs for - pub zone: String, -} - -#[derive(Deserialize, JsonSchema)] -pub struct SledDiagnosticsLogsDownloadQueryParam { - /// The max number of rotated logs to include in the final support bundle - pub max_rotated: usize, -} - -/// Path parameters for Disk requests (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct DiskPathParam { - pub disk_id: Uuid, -} - -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -pub struct ArtifactConfig { - pub generation: Generation, - pub artifacts: BTreeSet, -} - -impl Ledgerable for ArtifactConfig { - fn is_newer_than(&self, other: &ArtifactConfig) -> bool { - self.generation > other.generation - } - - // No need to do this, the generation number is provided externally. - fn generation_bump(&mut self) {} -} - -#[derive(Deserialize, JsonSchema)] -pub struct ArtifactPathParam { - pub sha256: ArtifactHash, -} - -#[derive(Deserialize, JsonSchema)] -pub struct ArtifactQueryParam { - pub generation: Generation, -} - -#[derive(Debug, Serialize, JsonSchema)] -pub struct ArtifactListResponse { - pub generation: Generation, - pub list: BTreeMap, -} - -#[derive(Deserialize, JsonSchema)] -pub struct ArtifactCopyFromDepotBody { - pub depot_base_url: String, -} - -#[derive(Debug, Serialize, JsonSchema)] -pub struct ArtifactCopyFromDepotResponse {} - -#[derive(Debug, Serialize, JsonSchema)] -pub struct ArtifactPutResponse { - /// The number of valid M.2 artifact datasets we found on the sled. There is - /// typically one of these datasets for each functional M.2. - pub datasets: usize, - - /// The number of valid writes to the M.2 artifact datasets. This should be - /// less than or equal to the number of artifact datasets. - pub successful_writes: usize, -} - -#[derive(Deserialize, JsonSchema)] -pub struct VmmIssueDiskSnapshotRequestPathParam { - pub propolis_id: PropolisUuid, - pub disk_id: Uuid, -} - -#[derive(Deserialize, JsonSchema)] -pub struct VmmIssueDiskSnapshotRequestBody { - pub snapshot_id: Uuid, -} - -#[derive(Serialize, JsonSchema)] -pub struct VmmIssueDiskSnapshotRequestResponse { - pub snapshot_id: Uuid, -} - -/// Path parameters for VPC requests (sled agent API) -#[derive(Deserialize, JsonSchema)] -pub struct VpcPathParam { - pub vpc_id: Uuid, -} - -/// Policy allowing an operator (via `omdb`) to control whether the switch zone -/// is started or stopped. -/// -/// This is an _extremely_ dicey operation in general; a stopped switch zone -/// leaves the rack inoperable! We are only adding this as a workaround and test -/// tool for handling sidecar resets; see -/// for background. -#[derive( - Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema, -)] -#[serde(tag = "policy", rename_all = "snake_case")] -pub enum OperatorSwitchZonePolicy { - /// Start the switch zone if a switch is present. - /// - /// This is the default policy. - StartIfSwitchPresent, - - /// Even if a switch zone is present, stop the switch zone. - StopDespiteSwitchPresence, -} - -/// Path parameters for Local Storage dataset related requests -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct LocalStoragePathParam { - pub zpool_id: ExternalZpoolUuid, - pub dataset_id: DatasetUuid, -} - -/// Dataset and Volume details for a Local Storage dataset ensure request -#[derive(Clone, Serialize, Deserialize, JsonSchema)] -pub struct LocalStorageDatasetEnsureRequest { - /// Size of the parent dataset - pub dataset_size: ByteCount, - - /// Size of the zvol - pub volume_size: ByteCount, -} diff --git a/sled-agent/api/src/v6.rs b/sled-agent/api/src/v6.rs deleted file mode 100644 index 8835bfc1870..00000000000 --- a/sled-agent/api/src/v6.rs +++ /dev/null @@ -1,160 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! Sled agent types (version 6) -//! -//! Version 6 types (before multicast support was added in version 7). - -use crate::v8; -use omicron_common::api::external; -use omicron_common::api::external::Hostname; -use omicron_common::api::internal::nexus::HostIdentifier; -use omicron_common::api::internal::nexus::VmmRuntimeState; -use omicron_common::api::internal::shared::DhcpConfig; -use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; -use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; -use omicron_uuid_kinds::InstanceUuid; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use sled_agent_types::instance::InstanceMetadata; -use sled_agent_types::instance::VmmSpec; -use std::collections::HashSet; -use std::net::IpAddr; -use std::net::SocketAddr; -use uuid::Uuid; - -/// The body of a request to ensure that a instance and VMM are known to a sled -/// agent (version 6, before multicast support). -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct InstanceEnsureBody { - /// The virtual hardware configuration this virtual machine should have when - /// it is started. - pub vmm_spec: VmmSpec, - - /// Information about the sled-local configuration that needs to be - /// established to make the VM's virtual hardware fully functional. - pub local_config: InstanceSledLocalConfig, - - /// The initial VMM runtime state for the VMM being registered. - pub vmm_runtime: VmmRuntimeState, - - /// The ID of the instance for which this VMM is being created. - pub instance_id: InstanceUuid, - - /// The ID of the migration in to this VMM, if this VMM is being - /// ensured is part of a migration in. If this is `None`, the VMM is not - /// being created due to a migration. - pub migration_id: Option, - - /// The address at which this VMM should serve a Propolis server API. - pub propolis_addr: SocketAddr, - - /// Metadata used to track instance statistics. - pub metadata: InstanceMetadata, -} - -/// Describes sled-local configuration that a sled-agent must establish to make -/// the instance's virtual hardware fully functional (version 6, before multicast). -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct InstanceSledLocalConfig { - pub hostname: Hostname, - pub nics: Vec, - pub source_nat: SourceNatConfig, - /// Zero or more external IP addresses (either floating or ephemeral), - /// provided to an instance to allow inbound connectivity. - pub ephemeral_ip: Option, - pub floating_ips: Vec, - pub firewall_rules: Vec, - pub dhcp_config: DhcpConfig, -} - -impl From for v8::InstanceSledLocalConfig { - fn from(v6: InstanceSledLocalConfig) -> Self { - let InstanceSledLocalConfig { - hostname, - nics, - source_nat, - ephemeral_ip, - floating_ips, - firewall_rules, - dhcp_config, - } = v6; - let firewall_rules = - firewall_rules.into_iter().map(Into::into).collect(); - - Self { - hostname, - nics, - source_nat, - ephemeral_ip, - floating_ips, - multicast_groups: Vec::new(), - firewall_rules, - dhcp_config, - } - } -} - -impl From for v8::InstanceEnsureBody { - fn from(v6: InstanceEnsureBody) -> Self { - let InstanceEnsureBody { - vmm_spec, - local_config, - vmm_runtime, - instance_id, - migration_id, - propolis_addr, - metadata, - } = v6; - - Self { - vmm_spec, - local_config: local_config.into(), - vmm_runtime, - instance_id, - migration_id, - propolis_addr, - metadata, - } - } -} - -/// VPC firewall rule after object name resolution has been performed by Nexus -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct ResolvedVpcFirewallRule { - pub status: external::VpcFirewallRuleStatus, - pub direction: external::VpcFirewallRuleDirection, - pub targets: Vec, - pub filter_hosts: Option>, - pub filter_ports: Option>, - pub filter_protocols: Option>, - pub action: external::VpcFirewallRuleAction, - pub priority: external::VpcFirewallRulePriority, -} - -impl From for v8::ResolvedVpcFirewallRule { - fn from(v6: ResolvedVpcFirewallRule) -> Self { - let ResolvedVpcFirewallRule { - status, - direction, - targets, - filter_hosts, - filter_ports, - filter_protocols, - action, - priority, - } = v6; - Self { - status, - direction, - targets, - filter_hosts, - filter_ports, - filter_protocols, - action, - priority, - } - } -} diff --git a/sled-agent/config-reconciler/Cargo.toml b/sled-agent/config-reconciler/Cargo.toml index eb2910a70ba..d66d9cfcef2 100644 --- a/sled-agent/config-reconciler/Cargo.toml +++ b/sled-agent/config-reconciler/Cargo.toml @@ -24,7 +24,7 @@ iddqd.workspace = true illumos-utils.workspace = true installinator-common.workspace = true key-manager.workspace = true -nexus-sled-agent-shared.workspace = true +sled-agent-types-versions.workspace = true ntp-admin-client.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true diff --git a/sled-agent/config-reconciler/src/dataset_serialization_task.rs b/sled-agent/config-reconciler/src/dataset_serialization_task.rs index 63cd6a2cf27..9abad836c09 100644 --- a/sled-agent/config-reconciler/src/dataset_serialization_task.rs +++ b/sled-agent/config-reconciler/src/dataset_serialization_task.rs @@ -26,14 +26,14 @@ use illumos_utils::zfs::DestroyDatasetError; use illumos_utils::zfs::Mountpoint; use illumos_utils::zfs::WhichDatasets; use illumos_utils::zfs::Zfs; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::OrphanedDataset; use omicron_common::disk::DatasetConfig; use omicron_common::disk::DatasetKind; use omicron_common::disk::DatasetName; use omicron_common::disk::SharedDatasetConfig; use omicron_common::zpool_name::ZpoolName; use omicron_uuid_kinds::DatasetUuid; +use sled_agent_types::inventory::InventoryDataset; +use sled_agent_types::inventory::OrphanedDataset; use sled_storage::config::MountConfig; use sled_storage::dataset::CRYPT_DATASET; use sled_storage::dataset::ZONE_DATASET; diff --git a/sled-agent/config-reconciler/src/handle.rs b/sled-agent/config-reconciler/src/handle.rs index d921aea7845..86c275a81ff 100644 --- a/sled-agent/config-reconciler/src/handle.rs +++ b/sled-agent/config-reconciler/src/handle.rs @@ -5,14 +5,14 @@ use camino::Utf8PathBuf; use illumos_utils::zpool::PathInPool; use key_manager::StorageKeyRequester; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::InventoryDisk; -use nexus_sled_agent_shared::inventory::InventoryZpool; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; use omicron_common::disk::DatasetName; -use sled_agent_api::ArtifactConfig; +use sled_agent_types::artifact::ArtifactConfig; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::InventoryDataset; +use sled_agent_types::inventory::InventoryDisk; +use sled_agent_types::inventory::InventoryZpool; +use sled_agent_types::inventory::OmicronSledConfig; use sled_storage::config::MountConfig; use sled_storage::disk::Disk; use sled_storage::nested_dataset::NestedDatasetConfig; diff --git a/sled-agent/config-reconciler/src/host_phase_2.rs b/sled-agent/config-reconciler/src/host_phase_2.rs index 92a44d50026..b2bd07e5488 100644 --- a/sled-agent/config-reconciler/src/host_phase_2.rs +++ b/sled-agent/config-reconciler/src/host_phase_2.rs @@ -11,11 +11,11 @@ use crate::SledAgentArtifactStore; use camino::Utf8Path; use camino::Utf8PathBuf; use installinator_common::RawDiskWriter; -use nexus_sled_agent_shared::inventory::BootPartitionContents as BootPartitionContentsInventory; -use nexus_sled_agent_shared::inventory::BootPartitionDetails; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredContents; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; use omicron_common::disk::M2Slot; +use sled_agent_types::inventory::BootPartitionContents as BootPartitionContentsInventory; +use sled_agent_types::inventory::BootPartitionDetails; +use sled_agent_types::inventory::HostPhase2DesiredContents; +use sled_agent_types::inventory::HostPhase2DesiredSlots; use sled_agent_types::zone_images::ResolverStatus; use sled_hardware::PooledDiskError; use slog::Logger; @@ -596,7 +596,7 @@ pub enum ImageHeaderParseError { mod boot_image_header { use super::ImageHeaderParseError; use bytes::Buf as _; - use nexus_sled_agent_shared::inventory::BootImageHeader; + use sled_agent_types::inventory::BootImageHeader; pub(super) const SIZE: usize = 4096; pub(super) const DATASET_NAME_SIZE: usize = 128; diff --git a/sled-agent/config-reconciler/src/ledger.rs b/sled-agent/config-reconciler/src/ledger.rs index 82056502d8f..f47e7d01081 100644 --- a/sled-agent/config-reconciler/src/ledger.rs +++ b/sled-agent/config-reconciler/src/ledger.rs @@ -7,13 +7,13 @@ use camino::Utf8PathBuf; use dropshot::HttpError; use legacy_configs::convert_legacy_ledgers; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; use omicron_common::api::external::Generation; use omicron_common::ledger; use omicron_common::ledger::Ledger; -use sled_agent_api::ArtifactConfig; +use sled_agent_types::artifact::ArtifactConfig; +use sled_agent_types::inventory::HostPhase2DesiredSlots; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::OmicronZoneImageSource; use slog::Logger; use slog::error; use slog::info; @@ -30,7 +30,7 @@ use tufaceous_artifact::ArtifactHash; use crate::InternalDisksReceiver; use crate::SledAgentArtifactStore; -use crate::ledger::legacy_configs::try_convert_v9_sled_config; +use crate::ledger::legacy_configs::try_convert_v4_sled_config; mod legacy_configs; @@ -646,7 +646,7 @@ async fn load_sled_config( // If we have no ledgered config, see if we can convert from the previous // version of the format. - if let Some(config) = try_convert_v9_sled_config(log, paths).await { + if let Some(config) = try_convert_v4_sled_config(log, paths).await { info!( log, "Ledger of sled config exists, but it was formatted as \ @@ -695,11 +695,6 @@ mod tests { use camino_tempfile::tempfile; use iddqd::IdOrdMap; use illumos_utils::zpool::ZpoolName; - use nexus_sled_agent_shared::inventory::HostPhase2DesiredContents; - use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; - use nexus_sled_agent_shared::inventory::OmicronZoneConfig; - use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; - use nexus_sled_agent_shared::inventory::OmicronZoneType; use omicron_common::disk::DiskIdentity; use omicron_common::disk::OmicronPhysicalDiskConfig; use omicron_test_utils::dev; @@ -710,6 +705,11 @@ mod tests { use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::HostPhase2DesiredContents; + use sled_agent_types::inventory::HostPhase2DesiredSlots; + use sled_agent_types::inventory::OmicronZoneConfig; + use sled_agent_types::inventory::OmicronZoneImageSource; + use sled_agent_types::inventory::OmicronZoneType; use sled_storage::config::MountConfig; use std::collections::BTreeSet; use std::time::Duration; diff --git a/sled-agent/config-reconciler/src/ledger/legacy_configs.rs b/sled-agent/config-reconciler/src/ledger/legacy_configs.rs index 9fc919196c6..e6f348e6f02 100644 --- a/sled-agent/config-reconciler/src/ledger/legacy_configs.rs +++ b/sled-agent/config-reconciler/src/ledger/legacy_configs.rs @@ -5,8 +5,6 @@ //! Module for converting older formats of the sled configuration files. use camino::Utf8PathBuf; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; use omicron_common::api::external::Generation; use omicron_common::disk::DatasetsConfig; use omicron_common::disk::OmicronPhysicalDisksConfig; @@ -14,9 +12,11 @@ use omicron_common::ledger::Ledger; use omicron_common::ledger::Ledgerable; use serde::Deserialize; use serde::Serialize; -use sled_agent_types::inventory::v9::OmicronSledConfig as OmicronSledConfigV9; -use sled_agent_types::inventory::v9::OmicronZoneConfig as OmicronZoneConfigV9; -use sled_agent_types::inventory::v10::OmicronSledConfig as OmicronSledConfigV10; +use sled_agent_types::inventory::HostPhase2DesiredSlots; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types_versions::v4::inventory::OmicronSledConfig as OmicronSledConfigV4; +use sled_agent_types_versions::v4::inventory::OmicronZoneConfig as OmicronZoneConfigV4; +use sled_agent_types_versions::v10::inventory::OmicronSledConfig as OmicronSledConfigV10; use slog::Logger; use slog::error; use slog::warn; @@ -28,7 +28,7 @@ const LEGACY_DISKS_LEDGER_FILENAME: &str = "omicron-physical-disks.json"; const LEGACY_DATASETS_LEDGER_FILENAME: &str = "omicron-datasets.json"; const LEGACY_ZONES_LEDGER_FILENAME: &str = "omicron-zones.json"; -/// Convert from version 8 of the sled-configuration to the current, if +/// Convert from version 4 of the sled-configuration to the current, if /// possible. The later version includes dual-stack private IP configuration in /// our internal network interface types. /// @@ -40,7 +40,7 @@ const LEGACY_ZONES_LEDGER_FILENAME: &str = "omicron-zones.json"; /// we do encounter that. Returning `None` is not correct, since that would /// incorrectly indicate that we have no config at all. We _must_ panic and rely /// on support correcting this (believed-to-be-impossible) situation. -pub(super) async fn try_convert_v9_sled_config( +pub(super) async fn try_convert_v4_sled_config( log: &Logger, datasets: Vec, ) -> Option { @@ -48,12 +48,12 @@ pub(super) async fn try_convert_v9_sled_config( Ledger::::new(log, datasets.clone()).await?; let new_config = OmicronSledConfigV10::try_from(old.into_inner().0).unwrap_or_else(|e| { panic!( - "Failed to convert OmicronSledConfigV9 to the OmicronSledConfigV10: {e}" + "Failed to convert OmicronSledConfigV4 to OmicronSledConfigV10: {e}" ) }); let new_config = new_config.try_into().unwrap_or_else(|e| { panic!( - "Failed to convert OmicronSledConfigV10 to the curent version: {e}" + "Failed to convert OmicronSledConfigV10 to the current version: {e}" ) }); write_converted_ledger( @@ -146,7 +146,7 @@ pub(super) async fn convert_legacy_ledgers( // again won't change the result. let sled_config = OmicronSledConfigV10::try_from(sled_config) .unwrap_or_else(|e| panic!( - "Failed to convert OmicronSledConfigV9 to OmicronSledConfigV10: {e}" + "Failed to convert OmicronSledConfigV4 to OmicronSledConfigV10: {e}" )); let sled_config = OmicronSledConfig::try_from(sled_config) .unwrap_or_else(|e| panic!( @@ -287,8 +287,8 @@ fn merge_old_configs( disks: OmicronPhysicalDisksConfig, datasets: DatasetsConfig, zones: OmicronZonesConfigLocal, -) -> OmicronSledConfigV9 { - OmicronSledConfigV9 { +) -> OmicronSledConfigV4 { + OmicronSledConfigV4 { // Take the zone generation as the overall config generation; this is // consistent with Reconfigurator's transition from three configs to // one. @@ -327,7 +327,7 @@ impl Ledgerable for OmicronZonesConfigLocal { #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr(test, derive(schemars::JsonSchema))] struct OmicronZoneConfigLocal { - zone: OmicronZoneConfigV9, + zone: OmicronZoneConfigV4, #[serde(rename = "root")] #[cfg_attr(test, schemars(with = "String"))] _root: Utf8PathBuf, @@ -335,7 +335,7 @@ struct OmicronZoneConfigLocal { #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(transparent)] -struct OmicronSledConfigLocal(OmicronSledConfigV9); +struct OmicronSledConfigLocal(OmicronSledConfigV4); impl Ledgerable for OmicronSledConfigLocal { fn is_newer_than(&self, other: &Self) -> bool { @@ -395,20 +395,20 @@ pub(super) mod tests { // Convert, which will rewrite the config as well. let converted = - try_convert_v9_sled_config(&logctx.log, vec![dst_file.clone()]) + try_convert_v4_sled_config(&logctx.log, vec![dst_file.clone()]) .await .expect("Should have found and converted v9 config"); // And make sure it matches the new, directly loaded and converted from // disk. - let new_as_v9: OmicronSledConfigV9 = serde_json::from_str( + let new_as_v4: OmicronSledConfigV4 = serde_json::from_str( tokio::fs::read_to_string(dst_file).await.unwrap().as_str(), ) .expect("successfully converted config"); - let new_as_v10 = OmicronSledConfigV10::try_from(new_as_v9) - .expect("successfully converted v9 config"); + let new_as_v10 = OmicronSledConfigV10::try_from(new_as_v4) + .expect("successfully converted v4 config to v10"); let new = OmicronSledConfig::try_from(new_as_v10) - .expect("successfully converted v10 config"); + .expect("successfully converted v10 config to current"); assert_eq!(new, converted); logctx.cleanup_successful(); } diff --git a/sled-agent/config-reconciler/src/mupdate_override.rs b/sled-agent/config-reconciler/src/mupdate_override.rs index 1931224f948..633e9332b9a 100644 --- a/sled-agent/config-reconciler/src/mupdate_override.rs +++ b/sled-agent/config-reconciler/src/mupdate_override.rs @@ -7,11 +7,11 @@ use crate::InternalDisks; use crate::host_phase_2::HostPhase2PreparedContents; use camino::Utf8PathBuf; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredContents; -use nexus_sled_agent_shared::inventory::OmicronZoneConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::zone_images::ZoneImageFileSource; +use sled_agent_types::inventory::HostPhase2DesiredContents; +use sled_agent_types::inventory::OmicronZoneConfig; +use sled_agent_types::inventory::OmicronZoneImageSource; +use sled_agent_types::inventory::ZoneKind; use sled_agent_types::zone_images::OmicronZoneFileSource; use sled_agent_types::zone_images::OmicronZoneImageLocation; use sled_agent_types::zone_images::PreparedOmicronZone; diff --git a/sled-agent/config-reconciler/src/raw_disks.rs b/sled-agent/config-reconciler/src/raw_disks.rs index 3708d2d0e51..16db769c2f4 100644 --- a/sled-agent/config-reconciler/src/raw_disks.rs +++ b/sled-agent/config-reconciler/src/raw_disks.rs @@ -6,8 +6,8 @@ //! [`RawDisk`]s sled-agent is aware of. use iddqd::IdOrdMap; -use nexus_sled_agent_shared::inventory::InventoryDisk; use omicron_common::disk::DiskIdentity; +use sled_agent_types::inventory::InventoryDisk; use sled_storage::disk::RawDisk; use slog::Logger; use slog::info; diff --git a/sled-agent/config-reconciler/src/reconciler_task.rs b/sled-agent/config-reconciler/src/reconciler_task.rs index 41db4bc7c29..b0260465f87 100644 --- a/sled-agent/config-reconciler/src/reconciler_task.rs +++ b/sled-agent/config-reconciler/src/reconciler_task.rs @@ -12,17 +12,17 @@ use iddqd::IdOrdMap; use illumos_utils::zpool::PathInPool; use illumos_utils::zpool::ZpoolOrRamdisk; use key_manager::StorageKeyRequester; -use nexus_sled_agent_shared::inventory::BootPartitionContents as BootPartitionContentsInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventory; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::OrphanedDataset; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideInventory; use omicron_common::disk::DatasetKind; use omicron_uuid_kinds::DatasetUuid; use omicron_uuid_kinds::OmicronZoneUuid; use omicron_uuid_kinds::PhysicalDiskUuid; +use sled_agent_types::inventory::BootPartitionContents as BootPartitionContentsInventory; +use sled_agent_types::inventory::ConfigReconcilerInventory; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; +use sled_agent_types::inventory::OmicronSledConfig; +use sled_agent_types::inventory::OrphanedDataset; +use sled_agent_types::inventory::RemoveMupdateOverrideInventory; use sled_storage::config::MountConfig; use sled_storage::dataset::LOCAL_STORAGE_DATASET; use sled_storage::dataset::U2_DEBUG_DATASET; diff --git a/sled-agent/config-reconciler/src/reconciler_task/datasets.rs b/sled-agent/config-reconciler/src/reconciler_task/datasets.rs index 28dcb217efc..2cae109c382 100644 --- a/sled-agent/config-reconciler/src/reconciler_task/datasets.rs +++ b/sled-agent/config-reconciler/src/reconciler_task/datasets.rs @@ -20,13 +20,13 @@ use iddqd::IdOrdMap; use iddqd::id_upcast; use illumos_utils::zpool::PathInPool; use illumos_utils::zpool::ZpoolOrRamdisk; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::OmicronZoneConfig; -use nexus_sled_agent_shared::inventory::OrphanedDataset; use omicron_common::disk::DatasetConfig; use omicron_common::disk::DatasetKind; use omicron_common::disk::DatasetName; use omicron_uuid_kinds::DatasetUuid; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types::inventory::OmicronZoneConfig; +use sled_agent_types::inventory::OrphanedDataset; use sled_storage::config::MountConfig; use sled_storage::dataset::ZONE_DATASET; use slog::Logger; diff --git a/sled-agent/config-reconciler/src/reconciler_task/external_disks.rs b/sled-agent/config-reconciler/src/reconciler_task/external_disks.rs index cb3b91ecb3b..6db0a195e7d 100644 --- a/sled-agent/config-reconciler/src/reconciler_task/external_disks.rs +++ b/sled-agent/config-reconciler/src/reconciler_task/external_disks.rs @@ -17,7 +17,6 @@ use illumos_utils::zfs::Zfs; use illumos_utils::zpool::Zpool; use illumos_utils::zpool::ZpoolName; use key_manager::StorageKeyRequester; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; use omicron_common::api::external::ByteCount; use omicron_common::disk::DiskManagementError; use omicron_common::disk::DiskVariant; @@ -25,6 +24,7 @@ use omicron_common::disk::OmicronPhysicalDiskConfig; use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::ZpoolUuid; use rand::distr::{Alphanumeric, SampleString}; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; use sled_storage::config::MountConfig; use sled_storage::dataset::DatasetError; use sled_storage::dataset::ZONE_DATASET; diff --git a/sled-agent/config-reconciler/src/reconciler_task/zones.rs b/sled-agent/config-reconciler/src/reconciler_task/zones.rs index 88163399d74..7a2e02b4e60 100644 --- a/sled-agent/config-reconciler/src/reconciler_task/zones.rs +++ b/sled-agent/config-reconciler/src/reconciler_task/zones.rs @@ -25,12 +25,12 @@ use illumos_utils::zone::AdmError; use illumos_utils::zone::Api as _; use illumos_utils::zone::DeleteAddressError; use illumos_utils::zone::Zones; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::OmicronZoneConfig; -use nexus_sled_agent_shared::inventory::OmicronZoneType; use ntp_admin_client::types::TimeSync; use omicron_common::address::Ipv6Subnet; use omicron_uuid_kinds::OmicronZoneUuid; +use sled_agent_types::inventory::ConfigReconcilerInventoryResult; +use sled_agent_types::inventory::OmicronZoneConfig; +use sled_agent_types::inventory::OmicronZoneType; use sled_agent_types::zone_images::MupdateOverrideReadError; use sled_agent_types::zone_images::OmicronZoneImageLocation; use sled_agent_types::zone_images::PreparedOmicronZone; @@ -1280,9 +1280,6 @@ mod tests { use illumos_utils::zpool::PathInPool; use illumos_utils::zpool::ZpoolName; use illumos_utils::zpool::ZpoolOrRamdisk; - use nexus_sled_agent_shared::inventory::OmicronZoneDataset; - use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; - use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::address::SLED_PREFIX; use omicron_common::disk::DatasetConfig; use omicron_common::disk::DatasetKind; @@ -1295,6 +1292,9 @@ mod tests { use omicron_uuid_kinds::DatasetUuid; use omicron_uuid_kinds::MupdateOverrideUuid; use omicron_uuid_kinds::ZpoolUuid; + use sled_agent_types::inventory::OmicronZoneDataset; + use sled_agent_types::inventory::OmicronZoneImageSource; + use sled_agent_types::inventory::ZoneKind; use sled_agent_types::zone_images::MupdateOverrideStatus; use sled_agent_types::zone_images::OmicronZoneFileSource; use sled_agent_types::zone_images::RemoveMupdateOverrideBootSuccess; diff --git a/sled-agent/src/artifact_store.rs b/sled-agent/src/artifact_store.rs index d3f782a8602..b82a1ed6e70 100644 --- a/sled-agent/src/artifact_store.rs +++ b/sled-agent/src/artifact_store.rs @@ -37,11 +37,10 @@ use omicron_common::api::external::Generation; use omicron_common::ledger::Ledger; use repo_depot_api::*; use sha2::{Digest, Sha256}; -use sled_agent_api::{ - ArtifactConfig, ArtifactListResponse, ArtifactPutResponse, -}; use sled_agent_config_reconciler::ConfigReconcilerHandle; use sled_agent_config_reconciler::InternalDisksReceiver; +use sled_agent_types::artifact::ArtifactConfig; +use sled_agent_types::artifact::{ArtifactListResponse, ArtifactPutResponse}; use slog::{Logger, error, info}; use slog_error_chain::{InlineErrorChain, SlogInlineError}; use tokio::fs::File; @@ -896,7 +895,7 @@ mod test { use hex_literal::hex; use omicron_common::api::external::Generation; use omicron_test_utils::dev::test_setup_log; - use sled_agent_api::ArtifactConfig; + use sled_agent_types::artifact::ArtifactConfig; use tokio::io::AsyncReadExt; use tokio::sync::oneshot; use tokio::sync::watch; diff --git a/sled-agent/src/fakes/nexus.rs b/sled-agent/src/fakes/nexus.rs index 1af2546c13d..ea8775a5152 100644 --- a/sled-agent/src/fakes/nexus.rs +++ b/sled-agent/src/fakes/nexus.rs @@ -19,7 +19,7 @@ use omicron_common::api::internal::nexus::SledVmmState; use omicron_uuid_kinds::{OmicronZoneUuid, PropolisUuid, SledUuid}; use schemars::JsonSchema; use serde::Deserialize; -use sled_agent_api::VmmPathParam; +use sled_agent_types_versions::v1::instance::VmmPathParam; /// Implements a fake Nexus. /// diff --git a/sled-agent/src/hardware_monitor.rs b/sled-agent/src/hardware_monitor.rs index 56ba86510dc..8f91eb699b5 100644 --- a/sled-agent/src/hardware_monitor.rs +++ b/sled-agent/src/hardware_monitor.rs @@ -8,8 +8,8 @@ use crate::services::ServiceManager; use crate::sled_agent::SledAgent; -use sled_agent_api::OperatorSwitchZonePolicy; use sled_agent_config_reconciler::RawDisksSender; +use sled_agent_types::debug::OperatorSwitchZonePolicy; use sled_hardware::{HardwareManager, HardwareUpdate}; use sled_hardware_types::Baseboard; use sled_storage::disk::RawDisk; diff --git a/sled-agent/src/http_entrypoints.rs b/sled-agent/src/http_entrypoints.rs index a2b23eb66c5..62b6fb9b408 100644 --- a/sled-agent/src/http_entrypoints.rs +++ b/sled-agent/src/http_entrypoints.rs @@ -16,9 +16,6 @@ use dropshot::{ HttpResponseHeaders, HttpResponseOk, HttpResponseUpdatedNoContent, Path, Query, RequestContext, StreamingBody, TypedBody, }; -use nexus_sled_agent_shared::inventory::{ - Inventory, OmicronSledConfig, SledRole, -}; use omicron_common::api::external::Error; use omicron_common::api::internal::nexus::{DiskRuntimeState, SledVmmState}; use omicron_common::api::internal::shared::{ @@ -27,20 +24,44 @@ use omicron_common::api::internal::shared::{ }; use range_requests::PotentialRange; use sled_agent_api::*; +use sled_agent_types::artifact::{ + ArtifactConfig, ArtifactCopyFromDepotBody, ArtifactCopyFromDepotResponse, + ArtifactListResponse, ArtifactPathParam, ArtifactPutResponse, + ArtifactQueryParam, +}; use sled_agent_types::bootstore::BootstoreStatus; -use sled_agent_types::disk::DiskEnsureBody; +use sled_agent_types::dataset::{ + LocalStorageDatasetEnsureRequest, LocalStoragePathParam, +}; +use sled_agent_types::debug::OperatorSwitchZonePolicy; +use sled_agent_types::diagnostics::{ + SledDiagnosticsLogsDownloadPathParam, SledDiagnosticsLogsDownloadQueryParam, +}; +use sled_agent_types::disk::{DiskEnsureBody, DiskPathParam}; use sled_agent_types::early_networking::EarlyNetworkConfig; use sled_agent_types::firewall_rules::VpcFirewallRulesEnsureBody; use sled_agent_types::instance::{ - InstanceExternalIpBody, InstanceMulticastBody, VmmPutStateBody, - VmmPutStateResponse, VmmUnregisterResponse, + InstanceEnsureBody, InstanceExternalIpBody, InstanceMulticastBody, + VmmIssueDiskSnapshotRequestBody, VmmIssueDiskSnapshotRequestPathParam, + VmmIssueDiskSnapshotRequestResponse, VmmPathParam, VmmPutStateBody, + VmmPutStateResponse, VmmUnregisterResponse, VpcPathParam, }; +use sled_agent_types::inventory::{Inventory, OmicronSledConfig}; use sled_agent_types::probes::ProbeSet; use sled_agent_types::sled::AddSledRequest; +use sled_agent_types::support_bundle::{ + RangeRequestHeaders, SupportBundleFilePathParam, + SupportBundleFinalizeQueryParams, SupportBundleListPathParam, + SupportBundleMetadata, SupportBundlePathParam, + SupportBundleTransferQueryParams, +}; use sled_agent_types::zone_bundle::{ - BundleUtilization, CleanupContext, CleanupCount, CleanupPeriod, - StorageLimit, ZoneBundleId, ZoneBundleMetadata, + BundleUtilization, CleanupContext, CleanupContextUpdate, CleanupCount, + CleanupPeriod, StorageLimit, ZoneBundleFilter, ZoneBundleId, + ZoneBundleMetadata, ZonePathParam, }; +// Fixed identifiers for prior versions only +use sled_agent_types_versions::v1; use sled_diagnostics::{ SledDiagnosticsCommandHttpOutput, SledDiagnosticsQueryOutput, }; @@ -482,9 +503,9 @@ impl SledAgentApi for SledAgentImpl { Ok(HttpResponseUpdatedNoContent()) } - async fn sled_role_get( + async fn sled_role_get_v1( rqctx: RequestContext, - ) -> Result, HttpError> { + ) -> Result, HttpError> { let sa = rqctx.context(); Ok(HttpResponseOk(sa.get_role())) } @@ -492,7 +513,7 @@ impl SledAgentApi for SledAgentImpl { async fn vmm_register( rqctx: RequestContext, path_params: Path, - body: TypedBody, + body: TypedBody, ) -> Result, HttpError> { let sa = rqctx.context(); let propolis_id = path_params.into_inner().propolis_id; @@ -1012,11 +1033,11 @@ impl SledAgentApi for SledAgentImpl { async fn support_logs_download( request_context: RequestContext, - path_params: Path, + path_params: Path, query_params: Query, ) -> Result, HttpError> { let sa = request_context.context(); - let SledDiagnosticsLogsDownloadPathParm { zone } = + let SledDiagnosticsLogsDownloadPathParam { zone } = path_params.into_inner(); let SledDiagnosticsLogsDownloadQueryParam { max_rotated } = query_params.into_inner(); @@ -1027,10 +1048,12 @@ impl SledAgentApi for SledAgentImpl { .map_err(HttpError::from) } - async fn chicken_switch_destroy_orphaned_datasets_get( + async fn chicken_switch_destroy_orphaned_datasets_get_v1( _request_context: RequestContext, - ) -> Result, HttpError> - { + ) -> Result< + HttpResponseOk, + HttpError, + > { // This API has been removed, but we still provide an endpoint for // backwards compatibility. Only `omdb` ever called this endpoint, so we // could probably just always return an error, but we can at least @@ -1038,16 +1061,16 @@ impl SledAgentApi for SledAgentImpl { // and always attempt to destroy orphans, so we can just claim the // chicken switch is always in that state. let destroy_orphans = true; - Ok(HttpResponseOk(ChickenSwitchDestroyOrphanedDatasets { + Ok(HttpResponseOk(v1::debug::ChickenSwitchDestroyOrphanedDatasets { destroy_orphans, })) } - async fn chicken_switch_destroy_orphaned_datasets_put( + async fn chicken_switch_destroy_orphaned_datasets_put_v1( _request_context: RequestContext, - body: TypedBody, + body: TypedBody, ) -> Result { - let ChickenSwitchDestroyOrphanedDatasets { destroy_orphans } = + let v1::debug::ChickenSwitchDestroyOrphanedDatasets { destroy_orphans } = body.into_inner(); // This API has been removed, but we still provide an endpoint for diff --git a/sled-agent/src/rack_setup/plan/service.rs b/sled-agent/src/rack_setup/plan/service.rs index 09b65c379b2..f511f1de056 100644 --- a/sled-agent/src/rack_setup/plan/service.rs +++ b/sled-agent/src/rack_setup/plan/service.rs @@ -15,9 +15,6 @@ use internal_dns_types::config::{ DnsConfigBuilder, DnsConfigParams, Host, Zone, }; use internal_dns_types::names::ServiceName; -use nexus_sled_agent_shared::inventory::{ - Inventory, OmicronZoneDataset, SledRole, -}; use nexus_types::deployment::{ Blueprint, BlueprintDatasetConfig, BlueprintDatasetDisposition, BlueprintHostPhase2DesiredSlots, BlueprintPhysicalDiskConfig, @@ -63,6 +60,7 @@ use serde::{Deserialize, Serialize}; use sled_agent_client::{ Client as SledAgentClient, Error as SledAgentError, types as SledAgentTypes, }; +use sled_agent_types::inventory::{Inventory, OmicronZoneDataset, SledRole}; use sled_agent_types::rack_init::RackInitializeRequest as Config; use sled_agent_types::sled::StartSledAgentRequest; use slog::Logger; @@ -1301,14 +1299,14 @@ impl ServicePortBuilder { #[cfg(test)] mod tests { use super::*; - use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryStatus; - use nexus_sled_agent_shared::inventory::SledCpuFamily; - use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; use omicron_common::address::IpRange; use omicron_common::api::external::ByteCount; use omicron_common::api::internal::shared::AllowedSourceIps; use omicron_common::api::internal::shared::RackNetworkConfig; use oxnet::Ipv6Net; + use sled_agent_types::inventory::ConfigReconcilerInventoryStatus; + use sled_agent_types::inventory::SledCpuFamily; + use sled_agent_types::inventory::ZoneImageResolverInventory; use sled_agent_types::rack_init::BootstrapAddressDiscovery; use sled_agent_types::rack_init::RecoverySiloConfig; use sled_hardware_types::Baseboard; @@ -1493,7 +1491,7 @@ mod tests { const DISK_COUNT: usize = 10; let disks: Vec<_> = (0..DISK_COUNT) - .map(|i| nexus_sled_agent_shared::inventory::InventoryDisk { + .map(|i| sled_agent_types::inventory::InventoryDisk { identity: omicron_common::disk::DiskIdentity { vendor: "vendor".to_string(), model: "model".to_string(), diff --git a/sled-agent/src/rack_setup/service.rs b/sled-agent/src/rack_setup/service.rs index c3ff6e3e750..efc282eeb4a 100644 --- a/sled-agent/src/rack_setup/service.rs +++ b/sled-agent/src/rack_setup/service.rs @@ -84,10 +84,6 @@ use itertools::Itertools; use nexus_lockstep_client::{ Client as NexusClient, Error as NexusError, types as NexusTypes, }; -use nexus_sled_agent_shared::inventory::{ - ConfigReconcilerInventoryResult, HostPhase2DesiredSlots, OmicronSledConfig, - OmicronZoneConfig, OmicronZoneType, OmicronZonesConfig, -}; use nexus_types::deployment::{BlueprintZoneType, blueprint_zone_type}; use ntp_admin_client::{ Client as NtpAdminClient, Error as NtpAdminError, types::TimeSync, @@ -113,6 +109,10 @@ use sled_agent_config_reconciler::InternalDisksReceiver; use sled_agent_types::early_networking::{ EarlyNetworkConfig, EarlyNetworkConfigBody, }; +use sled_agent_types::inventory::{ + ConfigReconcilerInventoryResult, HostPhase2DesiredSlots, OmicronSledConfig, + OmicronZoneConfig, OmicronZoneType, OmicronZonesConfig, +}; use sled_agent_types::rack_init::{ BootstrapAddressDiscovery, RackInitializeRequest as Config, RackInitializeRequestParams, @@ -1723,16 +1723,16 @@ mod test { use super::*; use crate::rack_setup::plan::service::{Plan as ServicePlan, SledInfo}; use nexus_reconfigurator_blippy::{Blippy, BlippyReportSortKey}; - use nexus_sled_agent_shared::inventory::{ - Baseboard, ConfigReconcilerInventoryStatus, Inventory, InventoryDisk, - OmicronZoneType, SledCpuFamily, SledRole, ZoneImageResolverInventory, - }; use omicron_common::{ address::{Ipv6Subnet, SLED_PREFIX, get_sled_address}, api::external::{ByteCount, Generation}, disk::{DiskIdentity, DiskVariant}, }; use omicron_uuid_kinds::SledUuid; + use sled_agent_types::inventory::{ + Baseboard, ConfigReconcilerInventoryStatus, Inventory, InventoryDisk, + OmicronZoneType, SledCpuFamily, SledRole, ZoneImageResolverInventory, + }; fn make_sled_info( sled_id: SledUuid, diff --git a/sled-agent/src/services.rs b/sled-agent/src/services.rs index 2277aab5acf..a240558317c 100644 --- a/sled-agent/src/services.rs +++ b/sled-agent/src/services.rs @@ -60,11 +60,10 @@ use internal_dns_resolver::Resolver; use internal_dns_types::names::BOUNDARY_NTP_DNS_NAME; use internal_dns_types::names::DNS_ZONE; use nexus_config::{ConfigDropshotWithTls, DeploymentConfig}; -use nexus_sled_agent_shared::inventory::{ - OmicronZoneConfig, OmicronZoneType, ZoneKind, -}; +use omicron_common::address::AZ_PREFIX; use omicron_common::address::DENDRITE_PORT; use omicron_common::address::LLDP_PORT; +use omicron_common::address::MAX_PORT; use omicron_common::address::MGS_PORT; use omicron_common::address::NTP_ADMIN_PORT; use omicron_common::address::RACK_PREFIX; @@ -72,7 +71,6 @@ use omicron_common::address::SLED_PREFIX; use omicron_common::address::TFPORTD_PORT; use omicron_common::address::WICKETD_NEXUS_PROXY_PORT; use omicron_common::address::WICKETD_PORT; -use omicron_common::address::{AZ_PREFIX, MAX_PORT}; use omicron_common::address::{BOOTSTRAP_ARTIFACT_PORT, COCKROACH_ADMIN_PORT}; use omicron_common::address::{ CLICKHOUSE_ADMIN_PORT, CLICKHOUSE_TCP_PORT, @@ -91,6 +89,9 @@ use omicron_common::backoff::{ use omicron_common::disk::{DatasetKind, DatasetName}; use omicron_ddm_admin_client::DdmError; use omicron_uuid_kinds::OmicronZoneUuid; +use sled_agent_types::inventory::{ + OmicronZoneConfig, OmicronZoneType, ZoneKind, +}; use sled_agent_types::sled::SWITCH_ZONE_BASEBOARD_FILE; use sled_agent_types::zone_images::{ MupdateOverrideReadError, PreparedOmicronZone, diff --git a/sled-agent/src/sim/http_entrypoints.rs b/sled-agent/src/sim/http_entrypoints.rs index c55e6df7c5d..b38b41e56e5 100644 --- a/sled-agent/src/sim/http_entrypoints.rs +++ b/sled-agent/src/sim/http_entrypoints.rs @@ -24,9 +24,6 @@ use dropshot::RequestContext; use dropshot::StreamingBody; use dropshot::TypedBody; use dropshot::endpoint; -use nexus_sled_agent_shared::inventory::Inventory; -use nexus_sled_agent_shared::inventory::OmicronSledConfig; -use nexus_sled_agent_shared::inventory::SledRole; use omicron_common::api::internal::nexus::DiskRuntimeState; use omicron_common::api::internal::nexus::SledVmmState; use omicron_common::api::internal::shared::ExternalIpGatewayMap; @@ -39,22 +36,43 @@ use omicron_uuid_kinds::GenericUuid; use omicron_uuid_kinds::ZpoolUuid; use range_requests::PotentialRange; use sled_agent_api::*; +use sled_agent_types::artifact::{ + ArtifactConfig, ArtifactCopyFromDepotBody, ArtifactCopyFromDepotResponse, + ArtifactListResponse, ArtifactPathParam, ArtifactPutResponse, + ArtifactQueryParam, +}; use sled_agent_types::bootstore::BootstoreStatus; -use sled_agent_types::disk::DiskEnsureBody; +use sled_agent_types::dataset::{ + LocalStorageDatasetEnsureRequest, LocalStoragePathParam, +}; +use sled_agent_types::debug::OperatorSwitchZonePolicy; +use sled_agent_types::diagnostics::{ + SledDiagnosticsLogsDownloadPathParam, SledDiagnosticsLogsDownloadQueryParam, +}; +use sled_agent_types::disk::{DiskEnsureBody, DiskPathParam}; use sled_agent_types::early_networking::EarlyNetworkConfig; use sled_agent_types::firewall_rules::VpcFirewallRulesEnsureBody; -use sled_agent_types::instance::InstanceExternalIpBody; -use sled_agent_types::instance::InstanceMulticastBody; -use sled_agent_types::instance::VmmPutStateBody; -use sled_agent_types::instance::VmmPutStateResponse; -use sled_agent_types::instance::VmmUnregisterResponse; +use sled_agent_types::instance::{ + InstanceEnsureBody, InstanceExternalIpBody, InstanceMulticastBody, + VmmIssueDiskSnapshotRequestBody, VmmIssueDiskSnapshotRequestPathParam, + VmmIssueDiskSnapshotRequestResponse, VmmPathParam, VmmPutStateBody, + VmmPutStateResponse, VmmUnregisterResponse, VpcPathParam, +}; +use sled_agent_types::inventory::{Inventory, OmicronSledConfig}; use sled_agent_types::probes::ProbeSet; use sled_agent_types::sled::AddSledRequest; -use sled_agent_types::zone_bundle::BundleUtilization; -use sled_agent_types::zone_bundle::CleanupContext; -use sled_agent_types::zone_bundle::CleanupCount; -use sled_agent_types::zone_bundle::ZoneBundleId; -use sled_agent_types::zone_bundle::ZoneBundleMetadata; +use sled_agent_types::support_bundle::{ + RangeRequestHeaders, SupportBundleFilePathParam, + SupportBundleFinalizeQueryParams, SupportBundleListPathParam, + SupportBundleMetadata, SupportBundlePathParam, + SupportBundleTransferQueryParams, +}; +use sled_agent_types::zone_bundle::{ + BundleUtilization, CleanupContext, CleanupContextUpdate, CleanupCount, + ZoneBundleFilter, ZoneBundleId, ZoneBundleMetadata, ZonePathParam, +}; +// Fixed identifiers for prior versions only +use sled_agent_types_versions::v1; use sled_diagnostics::SledDiagnosticsQueryOutput; use std::collections::BTreeMap; use std::sync::Arc; @@ -87,7 +105,7 @@ impl SledAgentApi for SledAgentSimImpl { async fn vmm_register( rqctx: RequestContext, path_params: Path, - body: TypedBody, + body: TypedBody, ) -> Result, HttpError> { let sa = rqctx.context(); let propolis_id = path_params.into_inner().propolis_id; @@ -770,9 +788,9 @@ impl SledAgentApi for SledAgentSimImpl { method_unimplemented() } - async fn sled_role_get( + async fn sled_role_get_v1( _rqctx: RequestContext, - ) -> Result, HttpError> { + ) -> Result, HttpError> { method_unimplemented() } @@ -863,22 +881,24 @@ impl SledAgentApi for SledAgentSimImpl { async fn support_logs_download( _request_context: RequestContext, - _path_params: Path, + _path_params: Path, _query_params: Query, ) -> Result, HttpError> { method_unimplemented() } - async fn chicken_switch_destroy_orphaned_datasets_get( + async fn chicken_switch_destroy_orphaned_datasets_get_v1( _request_context: RequestContext, - ) -> Result, HttpError> - { + ) -> Result< + HttpResponseOk, + HttpError, + > { method_unimplemented() } - async fn chicken_switch_destroy_orphaned_datasets_put( + async fn chicken_switch_destroy_orphaned_datasets_put_v1( _request_context: RequestContext, - _body: TypedBody, + _body: TypedBody, ) -> Result { method_unimplemented() } diff --git a/sled-agent/src/sim/server.rs b/sled-agent/src/sim/server.rs index 188c152b984..260e4ef4c06 100644 --- a/sled-agent/src/sim/server.rs +++ b/sled-agent/src/sim/server.rs @@ -30,7 +30,6 @@ use nexus_lockstep_client::types::{ IpRange, Ipv4Range, Ipv6Range, RackInitializationRequest, RackNetworkConfigV2, }; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; use nexus_types::deployment::{ BlueprintPhysicalDiskConfig, BlueprintPhysicalDiskDisposition, BlueprintZoneImageSource, blueprint_zone_type, @@ -58,6 +57,7 @@ use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::ZpoolUuid; use oxnet::Ipv6Net; use rand::seq::IndexedRandom; +use sled_agent_types::inventory::OmicronZoneDataset; use sled_agent_types::rack_init::RecoverySiloConfig; use slog::{Drain, Logger, info}; use std::collections::HashMap; diff --git a/sled-agent/src/sim/sled_agent.rs b/sled-agent/src/sim/sled_agent.rs index b78b89472f9..d9d731bffd6 100644 --- a/sled-agent/src/sim/sled_agent.rs +++ b/sled-agent/src/sim/sled_agent.rs @@ -24,12 +24,6 @@ use chrono::Utc; use dropshot::Body; use dropshot::HttpError; use futures::Stream; -use nexus_sled_agent_shared::inventory::{ - ConfigReconcilerInventory, ConfigReconcilerInventoryStatus, - HostPhase2DesiredSlots, Inventory, InventoryDataset, InventoryDisk, - InventoryZpool, OmicronSledConfig, OmicronZonesConfig, SledRole, - ZoneImageResolverInventory, -}; use omicron_common::api::external::{ ByteCount, DiskState, Error, Generation, ResourceType, }; @@ -56,16 +50,22 @@ use propolis_client::{ Client as PropolisClient, types::InstanceInitializationMethod, }; use range_requests::PotentialRange; -use sled_agent_api::LocalStorageDatasetEnsureRequest; -use sled_agent_api::SupportBundleMetadata; +use sled_agent_types::dataset::LocalStorageDatasetEnsureRequest; use sled_agent_types::disk::DiskStateRequested; use sled_agent_types::early_networking::{ EarlyNetworkConfig, EarlyNetworkConfigBody, }; use sled_agent_types::instance::{ InstanceEnsureBody, InstanceExternalIpBody, InstanceMulticastMembership, - VmmPutStateResponse, VmmStateRequested, VmmUnregisterResponse, + VmmPutStateResponse, VmmSpecExt, VmmStateRequested, VmmUnregisterResponse, +}; +use sled_agent_types::inventory::{ + ConfigReconcilerInventory, ConfigReconcilerInventoryStatus, + HostPhase2DesiredSlots, Inventory, InventoryDataset, InventoryDisk, + InventoryZpool, OmicronSledConfig, OmicronZonesConfig, SledRole, + ZoneImageResolverInventory, }; +use sled_agent_types::support_bundle::SupportBundleMetadata; use slog::Logger; use std::collections::{HashMap, HashSet}; diff --git a/sled-agent/src/sim/storage.rs b/sled-agent/src/sim/storage.rs index 21744208510..92652801f06 100644 --- a/sled-agent/src/sim/storage.rs +++ b/sled-agent/src/sim/storage.rs @@ -42,7 +42,7 @@ use omicron_uuid_kinds::PhysicalDiskUuid; use omicron_uuid_kinds::ZpoolUuid; use propolis_client::VolumeConstructionRequest; use serde::Serialize; -use sled_agent_api::LocalStorageDatasetEnsureRequest; +use sled_agent_types::dataset::LocalStorageDatasetEnsureRequest; use sled_agent_types::support_bundle::NESTED_DATASET_NOT_FOUND; use sled_storage::nested_dataset::NestedDatasetConfig; use sled_storage::nested_dataset::NestedDatasetListOptions; diff --git a/sled-agent/src/sled_agent.rs b/sled-agent/src/sled_agent.rs index 8a53d9cb804..25bc71f0ef6 100644 --- a/sled-agent/src/sled_agent.rs +++ b/sled-agent/src/sled_agent.rs @@ -40,9 +40,6 @@ use illumos_utils::zfs::Zfs; use illumos_utils::zpool::PathInPool; use illumos_utils::zpool::ZpoolOrRamdisk; use itertools::Itertools as _; -use nexus_sled_agent_shared::inventory::{ - Inventory, OmicronSledConfig, SledRole, -}; use omicron_common::address::{ Ipv6Subnet, SLED_PREFIX, get_sled_address, get_switch_zone_address, }; @@ -68,12 +65,14 @@ use sled_agent_config_reconciler::{ InternalDisksReceiver, LedgerNewConfigError, LedgerTaskError, ReconcilerInventory, SledAgentArtifactStore, SledAgentFacilities, }; +use sled_agent_types::dataset::LocalStorageDatasetEnsureRequest; use sled_agent_types::disk::DiskStateRequested; use sled_agent_types::early_networking::EarlyNetworkConfig; use sled_agent_types::instance::{ InstanceEnsureBody, InstanceExternalIpBody, InstanceMulticastBody, VmmPutStateResponse, VmmStateRequested, VmmUnregisterResponse, }; +use sled_agent_types::inventory::{Inventory, OmicronSledConfig, SledRole}; use sled_agent_types::probes::ProbeCreate; use sled_agent_types::sled::{BaseboardId, StartSledAgentRequest}; use sled_agent_types::zone_bundle::{ @@ -1226,7 +1225,7 @@ impl SledAgent { &self, zpool_id: ExternalZpoolUuid, dataset_id: DatasetUuid, - request: sled_agent_api::LocalStorageDatasetEnsureRequest, + request: LocalStorageDatasetEnsureRequest, ) -> Result<(), HttpError> { // Ensure that the local storage dataset we want to use is still present let present = self @@ -1253,10 +1252,8 @@ impl SledAgent { let delegated_zvol = DelegatedZvol::LocalStorage { zpool_id, dataset_id }; - let sled_agent_api::LocalStorageDatasetEnsureRequest { - dataset_size, - volume_size, - } = request; + let LocalStorageDatasetEnsureRequest { dataset_size, volume_size } = + request; Zfs::ensure_dataset(DatasetEnsureArgs { name: &delegated_zvol.parent_dataset_name(), diff --git a/sled-agent/src/support_bundle/storage.rs b/sled-agent/src/support_bundle/storage.rs index 98e88eb8918..54cb6878f29 100644 --- a/sled-agent/src/support_bundle/storage.rs +++ b/sled-agent/src/support_bundle/storage.rs @@ -26,7 +26,6 @@ use omicron_uuid_kinds::ZpoolUuid; use range_requests::PotentialRange; use range_requests::SingleRange; use sha2::Digest; -use sled_agent_api::*; use sled_agent_config_reconciler::ConfigReconcilerHandle; use sled_agent_config_reconciler::DatasetTaskError; use sled_agent_config_reconciler::InventoryError; @@ -37,6 +36,9 @@ use sled_agent_config_reconciler::NestedDatasetMountError; use sled_agent_types::support_bundle::BUNDLE_FILE_NAME; use sled_agent_types::support_bundle::BUNDLE_TMP_FILE_NAME; use sled_agent_types::support_bundle::NESTED_DATASET_NOT_FOUND; +use sled_agent_types::support_bundle::{ + SupportBundleMetadata, SupportBundleState, +}; use sled_storage::nested_dataset::NestedDatasetConfig; use sled_storage::nested_dataset::NestedDatasetListOptions; use sled_storage::nested_dataset::NestedDatasetLocation; diff --git a/sled-agent/src/zone_bundle.rs b/sled-agent/src/zone_bundle.rs index 29c095cab24..da6121950ed 100644 --- a/sled-agent/src/zone_bundle.rs +++ b/sled-agent/src/zone_bundle.rs @@ -1522,7 +1522,7 @@ async fn run_cleanup( // Sort all the bundles in the current directory, using the priority // described in `context.priority`. - info.sort_by(|lhs, rhs| context.priority.compare_bundles(lhs, rhs)); + info.sort_by(|lhs, rhs| compare_bundles(&context.priority, lhs, rhs)); let current_usage = usages.get(&dir).unwrap(); // Remove bundles until we fall below the threshold. diff --git a/sled-agent/types/Cargo.toml b/sled-agent/types/Cargo.toml index b55a719cf70..c0f1dc38b3c 100644 --- a/sled-agent/types/Cargo.toml +++ b/sled-agent/types/Cargo.toml @@ -15,10 +15,6 @@ camino.workspace = true chrono.workspace = true daft.workspace = true iddqd.workspace = true -nexus-sled-agent-shared.workspace = true -# Note: we're trying to avoid a dependency from sled-agent-types to nexus-types -# because the correct direction of dependency is unclear. If there are types -# common to both, put them in `omicron-common` or `nexus-sled-agent-shared`. omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true @@ -29,6 +25,7 @@ serde.workspace = true serde_human_bytes.workspace = true serde_json.workspace = true sha3.workspace = true +sled-agent-types-versions.workspace = true sled-hardware-types.workspace = true slog.workspace = true slog-error-chain.workspace = true diff --git a/sled-agent/types/src/artifact.rs b/sled-agent/types/src/artifact.rs new file mode 100644 index 00000000000..a118f3c271b --- /dev/null +++ b/sled-agent/types/src/artifact.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Artifact types for sled-agent. + +pub use sled_agent_types_versions::latest::artifact::*; diff --git a/sled-agent/types/src/bootstore.rs b/sled-agent/types/src/bootstore.rs index 9c9e8257a47..ca3af543925 100644 --- a/sled-agent/types/src/bootstore.rs +++ b/sled-agent/types/src/bootstore.rs @@ -2,50 +2,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use std::{collections::BTreeSet, net::SocketAddrV6}; +//! Bootstore types for sled-agent. -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use sled_hardware_types::Baseboard; - -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct BootstoreStatus { - pub fsm_ledger_generation: u64, - pub network_config_ledger_generation: Option, - pub fsm_state: String, - pub peers: BTreeSet, - pub established_connections: Vec, - pub accepted_connections: BTreeSet, - pub negotiating_connections: BTreeSet, -} - -impl From for BootstoreStatus { - fn from(value: bootstore::schemes::v0::Status) -> Self { - BootstoreStatus { - fsm_ledger_generation: value.fsm_ledger_generation, - network_config_ledger_generation: value - .network_config_ledger_generation, - fsm_state: value.fsm_state.to_string(), - peers: value.peers, - established_connections: value - .connections - .into_iter() - .map(EstablishedConnection::from) - .collect(), - accepted_connections: value.accepted_connections, - negotiating_connections: value.negotiating_connections, - } - } -} - -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct EstablishedConnection { - pub baseboard: Baseboard, - pub addr: SocketAddrV6, -} - -impl From<(Baseboard, SocketAddrV6)> for EstablishedConnection { - fn from(value: (Baseboard, SocketAddrV6)) -> Self { - EstablishedConnection { baseboard: value.0, addr: value.1 } - } -} +pub use sled_agent_types_versions::latest::bootstore::*; diff --git a/sled-agent/types/src/dataset.rs b/sled-agent/types/src/dataset.rs new file mode 100644 index 00000000000..f34f9b1103e --- /dev/null +++ b/sled-agent/types/src/dataset.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Dataset types for sled-agent. + +pub use sled_agent_types_versions::latest::dataset::*; diff --git a/sled-agent/types/src/debug.rs b/sled-agent/types/src/debug.rs new file mode 100644 index 00000000000..fd3408d999e --- /dev/null +++ b/sled-agent/types/src/debug.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Debug types for sled-agent. + +pub use sled_agent_types_versions::latest::debug::*; diff --git a/sled-agent/types/src/diagnostics.rs b/sled-agent/types/src/diagnostics.rs new file mode 100644 index 00000000000..c78c1c7675b --- /dev/null +++ b/sled-agent/types/src/diagnostics.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Diagnostics types for sled-agent. + +pub use sled_agent_types_versions::latest::diagnostics::*; diff --git a/sled-agent/types/src/disk.rs b/sled-agent/types/src/disk.rs index 332f1a0c5c5..dc3bfc212ab 100644 --- a/sled-agent/types/src/disk.rs +++ b/sled-agent/types/src/disk.rs @@ -2,40 +2,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use omicron_common::api::internal::nexus::DiskRuntimeState; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use uuid::Uuid; +//! Disk types for sled-agent. -/// Sent from to a sled agent to establish the runtime state of a Disk -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct DiskEnsureBody { - /// Last runtime state of the Disk known to Nexus (used if the agent has - /// never seen this Disk before). - pub initial_runtime: DiskRuntimeState, - /// requested runtime state of the Disk - pub target: DiskStateRequested, -} - -/// Used to request a Disk state change -#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, JsonSchema)] -#[serde(rename_all = "lowercase", tag = "state", content = "instance")] -pub enum DiskStateRequested { - Detached, - Attached(Uuid), - Destroyed, - Faulted, -} - -impl DiskStateRequested { - /// Returns whether the requested state is attached to an Instance or not. - pub fn is_attached(&self) -> bool { - match self { - DiskStateRequested::Detached => false, - DiskStateRequested::Destroyed => false, - DiskStateRequested::Faulted => false, - - DiskStateRequested::Attached(_) => true, - } - } -} +pub use sled_agent_types_versions::latest::disk::*; diff --git a/sled-agent/types/src/early_networking.rs b/sled-agent/types/src/early_networking.rs index 73767ecf18c..55e4ff5f62c 100644 --- a/sled-agent/types/src/early_networking.rs +++ b/sled-agent/types/src/early_networking.rs @@ -4,617 +4,7 @@ //! Types for network setup required to bring up the control plane. -use std::str::FromStr; +pub use sled_agent_types_versions::latest::early_networking::*; -use bootstore::schemes::v0 as bootstore; -use omicron_common::api::internal::shared::RackNetworkConfig; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use slog::{Logger, warn}; - -/// Network configuration required to bring up the control plane -/// -/// The fields in this structure are those from -/// [`crate::rack_init::RackInitializeRequest`] necessary for use beyond RSS. -/// This is just for the initial rack configuration and cold boot purposes. -/// Updates come from Nexus. -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] -pub struct EarlyNetworkConfig { - // The current generation number of data as stored in CRDB. - // The initial generation is set during RSS time and then only mutated - // by Nexus. - pub generation: u64, - - // Which version of the data structure do we have. This is to help with - // deserialization and conversion in future updates. - pub schema_version: u32, - - // The actual configuration details - pub body: EarlyNetworkConfigBody, -} - -impl FromStr for EarlyNetworkConfig { - type Err = String; - - fn from_str(value: &str) -> Result { - #[derive(Deserialize)] - struct ShadowConfig { - generation: u64, - schema_version: u32, - body: EarlyNetworkConfigBody, - } - - let v2_err = match serde_json::from_str::(&value) { - Ok(cfg) => { - return Ok(EarlyNetworkConfig { - generation: cfg.generation, - schema_version: cfg.schema_version, - body: cfg.body, - }); - } - Err(e) => format!("unable to parse EarlyNetworkConfig: {e:?}"), - }; - // If we fail to parse the config as any known version, we return the - // error corresponding to the parse failure of the newest schema. - serde_json::from_str::(&value) - .map(|v1| EarlyNetworkConfig { - generation: v1.generation, - schema_version: Self::schema_version(), - body: v1.body.into(), - }) - .map_err(|_| v2_err) - } -} - -impl EarlyNetworkConfig { - pub fn schema_version() -> u32 { - 2 - } - - // Note: This currently only converts between v0 and v1 or deserializes v1 of - // `EarlyNetworkConfig`. - pub fn deserialize_bootstore_config( - log: &Logger, - config: &bootstore::NetworkConfig, - ) -> Result { - // Try to deserialize the latest version of the data structure (v2). If - // that succeeds we are done. - let v2_error = - match serde_json::from_slice::(&config.blob) { - Ok(val) => return Ok(val), - Err(error) => { - // Log this error and continue trying to deserialize older - // versions. - warn!( - log, - "Failed to deserialize EarlyNetworkConfig \ - as v2, trying next as v1: {}", - error, - ); - error - } - }; - - match serde_json::from_slice::( - &config.blob, - ) { - Ok(v1) => { - // Convert from v1 to v2 - return Ok(EarlyNetworkConfig { - generation: v1.generation, - schema_version: EarlyNetworkConfig::schema_version(), - body: v1.body.into(), - }); - } - Err(error) => { - // Log this error. - warn!( - log, - "Failed to deserialize EarlyNetworkConfig \ - as v1, trying next as v0: {}", - error - ); - } - }; - - match serde_json::from_slice::( - &config.blob, - ) { - Ok(val) => { - // Convert from v0 to v2 - return Ok(EarlyNetworkConfig { - generation: val.generation, - schema_version: 2, - body: EarlyNetworkConfigBody { - ntp_servers: val.ntp_servers, - rack_network_config: val.rack_network_config.map( - |v0_config| { - back_compat::RackNetworkConfigV0::to_v2( - val.rack_subnet, - v0_config, - ) - }, - ), - }, - }); - } - Err(error) => { - // Log this error. - warn!( - log, - "Failed to deserialize EarlyNetworkConfig as v0: {}", error, - ); - } - }; - - // If we fail to parse the config as any known version, we return the - // error corresponding to the parse failure of the newest schema. - Err(v2_error) - } -} - -/// This is the actual configuration of EarlyNetworking. -/// -/// We nest it below the "header" of `generation` and `schema_version` so that -/// we can perform partial deserialization of `EarlyNetworkConfig` to only read -/// the header and defer deserialization of the body once we know the schema -/// version. This is possible via the use of [`serde_json::value::RawValue`] in -/// future (post-v1) deserialization paths. -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] -pub struct EarlyNetworkConfigBody { - /// The external NTP server addresses. - pub ntp_servers: Vec, - - // Rack network configuration as delivered from RSS or Nexus - pub rack_network_config: Option, -} - -impl From for bootstore::NetworkConfig { - fn from(value: EarlyNetworkConfig) -> Self { - // Can this ever actually fail? - // We literally just deserialized the same data in RSS - let blob = serde_json::to_vec(&value).unwrap(); - - // Yes this is duplicated, but that seems fine. - let generation = value.generation; - - bootstore::NetworkConfig { generation, blob } - } -} - -/// Structures and routines used to maintain backwards compatibility. The -/// contents of this module should only be used to convert older data into the -/// current format, and not for any ongoing run-time operations. -pub mod back_compat { - use std::net::{Ipv4Addr, Ipv6Addr}; - - use omicron_common::api::{ - external::SwitchLocation, - internal::shared::{ - BfdPeerConfig, BgpConfig, BgpPeerConfig, PortConfigV2, PortFec, - PortSpeed, RackNetworkConfigV2, RouteConfig, UplinkAddressConfig, - }, - }; - use oxnet::{IpNet, Ipv4Net, Ipv6Net}; - - use super::*; - - #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] - pub struct EarlyNetworkConfigBodyV1 { - /// The external NTP server addresses. - pub ntp_servers: Vec, - - // Rack network configuration as delivered from RSS or Nexus - pub rack_network_config: Option, - } - - impl From for EarlyNetworkConfigBody { - fn from(v1: EarlyNetworkConfigBodyV1) -> Self { - EarlyNetworkConfigBody { - ntp_servers: v1.ntp_servers, - rack_network_config: v1 - .rack_network_config - .map(|v1_config| v1_config.into()), - } - } - } - - /// Deprecated, use `RackNetworkConfig` instead. Cannot actually deprecate due to - /// - /// - /// Our first version of `RackNetworkConfig`. If this exists in the bootstore, we - /// upgrade out of it into `RackNetworkConfigV1` or later versions if possible. - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] - pub(crate) struct RackNetworkConfigV0 { - // TODO: #3591 Consider making infra-ip ranges implicit for uplinks - /// First ip address to be used for configuring network infrastructure - pub infra_ip_first: Ipv4Addr, - /// Last ip address to be used for configuring network infrastructure - pub infra_ip_last: Ipv4Addr, - /// Uplinks for connecting the rack to external networks - pub uplinks: Vec, - } - - impl RackNetworkConfigV0 { - /// Convert from `RackNetworkConfigV0` to `RackNetworkConfigV1` - /// - /// We cannot use `From for `RackNetworkConfigV2` - /// because the `rack_subnet` field does not exist in `RackNetworkConfigV0` - /// and must be passed in from the `EarlyNetworkConfigV0` struct which - /// contains the `RackNetworkConfigV0` struct. - pub fn to_v2( - rack_subnet: Ipv6Addr, - v0: RackNetworkConfigV0, - ) -> RackNetworkConfigV2 { - RackNetworkConfigV2 { - rack_subnet: Ipv6Net::new(rack_subnet, 56).unwrap(), - infra_ip_first: v0.infra_ip_first, - infra_ip_last: v0.infra_ip_last, - ports: v0 - .uplinks - .into_iter() - .map(|uplink| PortConfigV2::from(uplink)) - .collect(), - bgp: vec![], - bfd: vec![], - } - } - } - - /// Deprecated, use PortConfigV2 instead. Cannot actually deprecate due to - /// - #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] - pub struct PortConfigV1 { - /// The set of routes associated with this port. - pub routes: Vec, - /// This port's addresses and optional vlan IDs - pub addresses: Vec, - /// Switch the port belongs to. - pub switch: SwitchLocation, - /// Nmae of the port this config applies to. - pub port: String, - /// Port speed. - pub uplink_port_speed: PortSpeed, - /// Port forward error correction type. - pub uplink_port_fec: PortFec, - /// BGP peers on this port - pub bgp_peers: Vec, - /// Whether or not to set autonegotiation - #[serde(default)] - pub autoneg: bool, - } - - impl From for PortConfigV2 { - fn from(v1: PortConfigV1) -> Self { - PortConfigV2 { - routes: v1.routes.clone(), - addresses: v1 - .addresses - .iter() - .map(|a| UplinkAddressConfig { address: *a, vlan_id: None }) - .collect(), - switch: v1.switch, - port: v1.port, - uplink_port_speed: v1.uplink_port_speed, - uplink_port_fec: Some(v1.uplink_port_fec), - bgp_peers: v1.bgp_peers.clone(), - autoneg: v1.autoneg, - lldp: None, - tx_eq: None, - } - } - } - - /// Deprecated, use PortConfigV2 instead. Cannot actually deprecate due to - /// - #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] - pub(crate) struct UplinkConfig { - /// Gateway address - pub gateway_ip: Ipv4Addr, - /// Switch to use for uplink - pub switch: SwitchLocation, - /// Switchport to use for external connectivity - pub uplink_port: String, - /// Speed for the Switchport - pub uplink_port_speed: PortSpeed, - /// Forward Error Correction setting for the uplink port - pub uplink_port_fec: PortFec, - /// IP Address and prefix (e.g., `192.168.0.1/16`) to apply to switchport - /// (must be in infra_ip pool) - pub uplink_cidr: Ipv4Net, - /// VLAN id to use for uplink - pub uplink_vid: Option, - /// RIB Priority - pub rib_priority: Option, - } - - impl From for PortConfigV2 { - fn from(value: UplinkConfig) -> Self { - PortConfigV2 { - routes: vec![RouteConfig { - destination: "0.0.0.0/0".parse().unwrap(), - nexthop: value.gateway_ip.into(), - vlan_id: value.uplink_vid, - rib_priority: value.rib_priority, - }], - addresses: vec![UplinkAddressConfig { - address: value.uplink_cidr.into(), - vlan_id: value.uplink_vid, - }], - switch: value.switch, - port: value.uplink_port, - uplink_port_speed: value.uplink_port_speed, - uplink_port_fec: Some(value.uplink_port_fec), - bgp_peers: vec![], - autoneg: false, - lldp: None, - tx_eq: None, - } - } - } - - /// Deprecated, use `RackNetworkConfig` instead. Cannot actually deprecate due to - /// - /// - /// Our second version of `RackNetworkConfig`. If this exists in the bootstore, - /// we upgrade out of it into `RackNetworkConfigV1` or later versions if - /// possible. - #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] - pub struct RackNetworkConfigV1 { - pub rack_subnet: Ipv6Net, - // TODO: #3591 Consider making infra-ip ranges implicit for uplinks - /// First ip address to be used for configuring network infrastructure - pub infra_ip_first: Ipv4Addr, - /// Last ip address to be used for configuring network infrastructure - pub infra_ip_last: Ipv4Addr, - /// Uplinks for connecting the rack to external networks - pub ports: Vec, - /// BGP configurations for connecting the rack to external networks - pub bgp: Vec, - /// BFD configuration for connecting the rack to external networks - #[serde(default)] - pub bfd: Vec, - } - - impl From for RackNetworkConfigV2 { - fn from(v1: RackNetworkConfigV1) -> Self { - RackNetworkConfigV2 { - rack_subnet: v1.rack_subnet, - infra_ip_first: v1.infra_ip_first, - infra_ip_last: v1.infra_ip_last, - ports: v1 - .ports - .into_iter() - .map(|ports| PortConfigV2::from(ports)) - .collect(), - bgp: v1.bgp.clone(), - bfd: v1.bfd.clone(), - } - } - } - - // The second production version of the `EarlyNetworkConfig`. - // - // If this version is in the bootstore than we need to convert it to - // `EarlyNetworkConfigV2`. - // - // Once we do this for all customers that have initialized racks with the - // old version we can go ahead and remove this type and its conversion code - // altogether. - #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] - pub struct EarlyNetworkConfigV1 { - // The current generation number of data as stored in CRDB. - // The initial generation is set during RSS time and then only mutated - // by Nexus. - pub generation: u64, - - // Which version of the data structure do we have. This is to help with - // deserialization and conversion in future updates. - pub schema_version: u32, - - // The actual configuration details - pub body: EarlyNetworkConfigBodyV1, - } - - // The first production version of the `EarlyNetworkConfig`. - // - // If this version is in the bootstore than we need to convert it to - // `EarlyNetworkConfigV2`. - // - // Once we do this for all customers that have initialized racks with the - // old version we can go ahead and remove this type and its conversion code - // altogether. - #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] - pub(crate) struct EarlyNetworkConfigV0 { - // The current generation number of data as stored in CRDB. - // The initial generation is set during RSS time and then only mutated - // by Nexus. - pub generation: u64, - - pub rack_subnet: Ipv6Addr, - - /// The external NTP server addresses. - pub ntp_servers: Vec, - - // Rack network configuration as delivered from RSS and only existing at - // generation 1 - pub rack_network_config: Option, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use std::net::Ipv4Addr; - use std::net::Ipv6Addr; - - use omicron_common::api::external::SwitchLocation; - use omicron_common::api::internal::shared::PortConfigV2; - use omicron_common::api::internal::shared::PortFec; - use omicron_common::api::internal::shared::PortSpeed; - use omicron_common::api::internal::shared::RackNetworkConfigV2; - use omicron_common::api::internal::shared::RouteConfig; - use omicron_common::api::internal::shared::UplinkAddressConfig; - use omicron_test_utils::dev::test_setup_log; - use oxnet::Ipv6Net; - - #[test] - fn serialized_early_network_config_v0_to_v2_conversion() { - let logctx = test_setup_log( - "serialized_early_network_config_v0_to_v2_conversion", - ); - let v0 = back_compat::EarlyNetworkConfigV0 { - generation: 1, - rack_subnet: Ipv6Addr::UNSPECIFIED, - ntp_servers: Vec::new(), - rack_network_config: Some(back_compat::RackNetworkConfigV0 { - infra_ip_first: Ipv4Addr::UNSPECIFIED, - infra_ip_last: Ipv4Addr::UNSPECIFIED, - uplinks: vec![back_compat::UplinkConfig { - gateway_ip: Ipv4Addr::UNSPECIFIED, - switch: SwitchLocation::Switch0, - uplink_port: "Port0".to_string(), - uplink_port_speed: PortSpeed::Speed100G, - uplink_port_fec: PortFec::None, - uplink_cidr: "192.168.0.1/16".parse().unwrap(), - uplink_vid: None, - rib_priority: None, - }], - }), - }; - - let v0_serialized = serde_json::to_vec(&v0).unwrap(); - let bootstore_conf = - bootstore::NetworkConfig { generation: 1, blob: v0_serialized }; - - let v2 = EarlyNetworkConfig::deserialize_bootstore_config( - &logctx.log, - &bootstore_conf, - ) - .unwrap(); - let v0_rack_network_config = v0.rack_network_config.unwrap(); - let uplink = v0_rack_network_config.uplinks[0].clone(); - let expected = EarlyNetworkConfig { - generation: 1, - schema_version: EarlyNetworkConfig::schema_version(), - body: EarlyNetworkConfigBody { - ntp_servers: v0.ntp_servers.clone(), - rack_network_config: Some(RackNetworkConfigV2 { - rack_subnet: Ipv6Net::new(v0.rack_subnet, 56).unwrap(), - infra_ip_first: v0_rack_network_config.infra_ip_first, - infra_ip_last: v0_rack_network_config.infra_ip_last, - ports: vec![PortConfigV2 { - routes: vec![RouteConfig { - destination: "0.0.0.0/0".parse().unwrap(), - nexthop: uplink.gateway_ip.into(), - vlan_id: None, - rib_priority: None, - }], - addresses: vec![UplinkAddressConfig { - address: uplink.uplink_cidr.into(), - vlan_id: None, - }], - switch: uplink.switch, - port: uplink.uplink_port, - uplink_port_speed: uplink.uplink_port_speed, - uplink_port_fec: Some(uplink.uplink_port_fec), - autoneg: false, - bgp_peers: vec![], - lldp: None, - tx_eq: None, - }], - bgp: vec![], - bfd: vec![], - }), - }, - }; - - assert_eq!(expected, v2); - - logctx.cleanup_successful(); - } - - #[test] - fn serialized_early_network_config_v1_to_v2_conversion() { - let logctx = test_setup_log( - "serialized_early_network_config_v1_to_v2_conversion", - ); - - let v1 = back_compat::EarlyNetworkConfigV1 { - generation: 1, - schema_version: 1, - body: back_compat::EarlyNetworkConfigBodyV1 { - ntp_servers: Vec::new(), - rack_network_config: Some(back_compat::RackNetworkConfigV1 { - rack_subnet: Ipv6Net::new(Ipv6Addr::UNSPECIFIED, 56) - .unwrap(), - infra_ip_first: Ipv4Addr::UNSPECIFIED, - infra_ip_last: Ipv4Addr::UNSPECIFIED, - ports: vec![back_compat::PortConfigV1 { - routes: vec![RouteConfig { - destination: "0.0.0.0/0".parse().unwrap(), - nexthop: "192.168.0.2".parse().unwrap(), - vlan_id: None, - rib_priority: None, - }], - addresses: vec!["192.168.0.1/16".parse().unwrap()], - switch: SwitchLocation::Switch0, - port: "Port0".to_string(), - uplink_port_speed: PortSpeed::Speed100G, - uplink_port_fec: PortFec::None, - bgp_peers: Vec::new(), - autoneg: false, - }], - bgp: Vec::new(), - bfd: Vec::new(), - }), - }, - }; - - let v1_serialized = serde_json::to_vec(&v1).unwrap(); - let bootstore_conf = - bootstore::NetworkConfig { generation: 1, blob: v1_serialized }; - - let v2 = EarlyNetworkConfig::deserialize_bootstore_config( - &logctx.log, - &bootstore_conf, - ) - .unwrap(); - let v1_rack_network_config = v1.body.rack_network_config.unwrap(); - let port = v1_rack_network_config.ports[0].clone(); - let expected = EarlyNetworkConfig { - generation: 1, - schema_version: EarlyNetworkConfig::schema_version(), - body: EarlyNetworkConfigBody { - ntp_servers: v1.body.ntp_servers.clone(), - rack_network_config: Some(RackNetworkConfigV2 { - rack_subnet: v1_rack_network_config.rack_subnet, - infra_ip_first: v1_rack_network_config.infra_ip_first, - infra_ip_last: v1_rack_network_config.infra_ip_last, - ports: vec![PortConfigV2 { - routes: port.routes.clone(), - addresses: vec![UplinkAddressConfig { - address: port.addresses[0], - vlan_id: None, - }], - switch: port.switch, - port: port.port, - uplink_port_speed: port.uplink_port_speed, - uplink_port_fec: Some(port.uplink_port_fec), - autoneg: false, - bgp_peers: vec![], - lldp: None, - tx_eq: None, - }], - bgp: vec![], - bfd: vec![], - }), - }, - }; - - assert_eq!(expected, v2); - - logctx.cleanup_successful(); - } -} +// Re-export back_compat module for bootstore serialization +pub use sled_agent_types_versions::v1::early_networking::back_compat; diff --git a/sled-agent/types/src/firewall_rules.rs b/sled-agent/types/src/firewall_rules.rs index d7cb22f976a..09f14b6ce9f 100644 --- a/sled-agent/types/src/firewall_rules.rs +++ b/sled-agent/types/src/firewall_rules.rs @@ -2,15 +2,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use omicron_common::api::{ - external, internal::shared::ResolvedVpcFirewallRule, -}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +//! Firewall rule types for the Sled Agent API. -/// Update firewall rules for a VPC -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct VpcFirewallRulesEnsureBody { - pub vni: external::Vni, - pub rules: Vec, -} +pub use sled_agent_types_versions::latest::firewall_rules::*; diff --git a/sled-agent/types/src/instance.rs b/sled-agent/types/src/instance.rs index 23f2bb7785f..843e7cd9532 100644 --- a/sled-agent/types/src/instance.rs +++ b/sled-agent/types/src/instance.rs @@ -4,232 +4,4 @@ //! Common instance-related types. -use omicron_common::api::external::Hostname; -use omicron_common::api::internal::nexus::SledVmmState; -use omicron_common::api::internal::nexus::VmmRuntimeState; -use omicron_common::api::internal::shared::DelegatedZvol; -use omicron_common::api::internal::shared::DhcpConfig; -use omicron_common::api::internal::shared::ExternalIpConfig; -use omicron_common::api::internal::shared::NetworkInterface; -use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; -use omicron_uuid_kinds::InstanceUuid; -use propolis_client::instance_spec::ComponentV0; -use propolis_client::instance_spec::CrucibleStorageBackend; -use propolis_client::instance_spec::FileStorageBackend; -use propolis_client::instance_spec::SpecKey; -use propolis_client::instance_spec::VirtioNetworkBackend; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use std::fmt; -use std::net::IpAddr; -use std::net::SocketAddr; -use uuid::Uuid; - -/// The body of a request to ensure that a instance and VMM are known to a sled -/// agent. -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct InstanceEnsureBody { - /// The virtual hardware configuration this virtual machine should have when - /// it is started. - pub vmm_spec: VmmSpec, - - /// Information about the sled-local configuration that needs to be - /// established to make the VM's virtual hardware fully functional. - pub local_config: InstanceSledLocalConfig, - - /// The initial VMM runtime state for the VMM being registered. - pub vmm_runtime: VmmRuntimeState, - - /// The ID of the instance for which this VMM is being created. - pub instance_id: InstanceUuid, - - /// The ID of the migration in to this VMM, if this VMM is being - /// ensured is part of a migration in. If this is `None`, the VMM is not - /// being created due to a migration. - pub migration_id: Option, - - /// The address at which this VMM should serve a Propolis server API. - pub propolis_addr: SocketAddr, - - /// Metadata used to track instance statistics. - pub metadata: InstanceMetadata, -} - -/// Describes sled-local configuration that a sled-agent must establish to make -/// the instance's virtual hardware fully functional. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct InstanceSledLocalConfig { - pub hostname: Hostname, - pub nics: Vec, - pub external_ips: Option, - pub multicast_groups: Vec, - pub firewall_rules: Vec, - pub dhcp_config: DhcpConfig, - pub delegated_zvols: Vec, -} - -/// Represents a multicast group membership for an instance. -#[derive( - Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, -)] -pub struct InstanceMulticastMembership { - pub group_ip: IpAddr, - // For Source-Specific Multicast (SSM) - pub sources: Vec, -} - -/// Request body for multicast group operations. -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub enum InstanceMulticastBody { - Join(InstanceMulticastMembership), - Leave(InstanceMulticastMembership), -} - -/// Metadata used to track statistics about an instance. -/// -// NOTE: The instance ID is not here, since it's already provided in other -// pieces of the instance-related requests. It is pulled from there when -// publishing metrics for the instance. -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct InstanceMetadata { - pub silo_id: Uuid, - pub project_id: Uuid, -} - -/// The body of a request to move a previously-ensured instance into a specific -/// runtime state. -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct VmmPutStateBody { - /// The state into which the instance should be driven. - pub state: VmmStateRequested, -} - -/// The response sent from a request to move an instance into a specific runtime -/// state. -#[derive(Debug, Serialize, Deserialize, JsonSchema)] -pub struct VmmPutStateResponse { - /// The current runtime state of the instance after handling the request to - /// change its state. If the instance's state did not change, this field is - /// `None`. - pub updated_runtime: Option, -} - -/// Requestable running state of an Instance. -/// -/// A subset of [`omicron_common::api::external::InstanceState`]. -#[derive(Copy, Clone, Debug, Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "snake_case", tag = "type", content = "value")] -pub enum VmmStateRequested { - /// Run this instance by migrating in from a previous running incarnation of - /// the instance. - MigrationTarget(InstanceMigrationTargetParams), - /// Start the instance if it is not already running. - Running, - /// Stop the instance. - Stopped, - /// Immediately reset the instance, as though it had stopped and immediately - /// began to run again. - Reboot, -} - -impl fmt::Display for VmmStateRequested { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.label()) - } -} - -impl VmmStateRequested { - fn label(&self) -> &str { - match self { - VmmStateRequested::MigrationTarget(_) => "migrating in", - VmmStateRequested::Running => "running", - VmmStateRequested::Stopped => "stopped", - VmmStateRequested::Reboot => "reboot", - } - } - - /// Returns true if the state represents a stopped Instance. - pub fn is_stopped(&self) -> bool { - match self { - VmmStateRequested::MigrationTarget(_) => false, - VmmStateRequested::Running => false, - VmmStateRequested::Stopped => true, - VmmStateRequested::Reboot => false, - } - } -} - -/// The response sent from a request to unregister an instance. -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct VmmUnregisterResponse { - /// The current state of the instance after handling the request to - /// unregister it. If the instance's state did not change, this field is - /// `None`. - pub updated_runtime: Option, -} - -/// Parameters used when directing Propolis to initialize itself via live -/// migration. -#[derive(Copy, Clone, Debug, Deserialize, Serialize, JsonSchema)] -pub struct InstanceMigrationTargetParams { - /// The address of the Propolis server that will serve as the migration - /// source. - pub src_propolis_addr: SocketAddr, -} - -/// Used to dynamically update external IPs attached to an instance. -#[derive( - Copy, Clone, Debug, Eq, PartialEq, Hash, Deserialize, JsonSchema, Serialize, -)] -#[serde(rename_all = "snake_case", tag = "type", content = "value")] -pub enum InstanceExternalIpBody { - Ephemeral(IpAddr), - Floating(IpAddr), -} - -/// Specifies the virtual hardware configuration of a new Propolis VMM in the -/// form of a Propolis instance specification. -/// -/// Sled-agent expects that when an instance spec is provided alongside an -/// `InstanceSledLocalConfig` to initialize a new instance, the NIC IDs in that -/// config's network interface list will match the IDs of the virtio network -/// backends in the instance spec. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct VmmSpec(pub propolis_client::instance_spec::InstanceSpecV0); - -impl VmmSpec { - pub fn crucible_backends( - &self, - ) -> impl Iterator { - self.0.components.iter().filter_map( - |(key, component)| match component { - ComponentV0::CrucibleStorageBackend(be) => Some((key, be)), - _ => None, - }, - ) - } - - pub fn viona_backends( - &self, - ) -> impl Iterator { - self.0.components.iter().filter_map( - |(key, component)| match component { - ComponentV0::VirtioNetworkBackend(be) => Some((key, be)), - _ => None, - }, - ) - } - - pub fn file_backends( - &self, - ) -> impl Iterator { - self.0.components.iter().filter_map( - |(key, component)| match component { - ComponentV0::FileStorageBackend(be) => Some((key, be)), - _ => None, - }, - ) - } -} +pub use sled_agent_types_versions::latest::instance::*; diff --git a/sled-agent/types/src/inventory.rs b/sled-agent/types/src/inventory.rs new file mode 100644 index 00000000000..185a52945e9 --- /dev/null +++ b/sled-agent/types/src/inventory.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Sled-agent types for inventory. + +pub use sled_agent_types_versions::latest::inventory::*; diff --git a/sled-agent/types/src/inventory/mod.rs b/sled-agent/types/src/inventory/mod.rs deleted file mode 100644 index ffb67ef1e7b..00000000000 --- a/sled-agent/types/src/inventory/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! Sled-agent types for inventory. -//! -//! This is intended to contain old versions of the inventory types, _which also -//! need to be public_. If the types don't need to be public, they should go -//! directly in the `sled-agent-api` crate. The current versions of the types -//! should be in `nexus-sled-agent-shared`. - -pub mod v10; -pub mod v9; diff --git a/sled-agent/types/src/inventory/v10.rs b/sled-agent/types/src/inventory/v10.rs deleted file mode 100644 index f48d84a0687..00000000000 --- a/sled-agent/types/src/inventory/v10.rs +++ /dev/null @@ -1,834 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! Version 10 of the sled-agent API types. - -use crate::instance::InstanceMetadata; -use crate::instance::InstanceMulticastMembership; -use crate::instance::VmmSpec; -use chrono::DateTime; -use chrono::Utc; -use iddqd::IdOrdItem; -use iddqd::IdOrdMap; -use iddqd::id_upcast; -use nexus_sled_agent_shared::inventory; -use nexus_sled_agent_shared::inventory::BootPartitionContents; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::InventoryDisk; -use nexus_sled_agent_shared::inventory::InventoryZpool; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; -use nexus_sled_agent_shared::inventory::OrphanedDataset; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideInventory; -use nexus_sled_agent_shared::inventory::SledRole; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; -use omicron_common::api::external; -use omicron_common::api::external::ByteCount; -use omicron_common::api::external::Generation; -use omicron_common::api::external::Hostname; -use omicron_common::api::internal::nexus::VmmRuntimeState; -use omicron_common::api::internal::shared::DelegatedZvol; -use omicron_common::api::internal::shared::DhcpConfig; -use omicron_common::api::internal::shared::ExternalIpConfig; -use omicron_common::api::internal::shared::NetworkInterface; -use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; -use omicron_common::api::internal::shared::SourceNatConfigGeneric; -use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; -use omicron_common::disk::DatasetConfig; -use omicron_common::disk::OmicronPhysicalDiskConfig; -use omicron_common::zpool_name::ZpoolName; -use omicron_uuid_kinds::DatasetUuid; -use omicron_uuid_kinds::InstanceUuid; -use omicron_uuid_kinds::MupdateOverrideUuid; -use omicron_uuid_kinds::OmicronZoneUuid; -use omicron_uuid_kinds::PhysicalDiskUuid; -use omicron_uuid_kinds::SledUuid; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use sled_hardware_types::Baseboard; -use sled_hardware_types::SledCpuFamily; -use std::collections::BTreeMap; -use std::net::IpAddr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV6; -use std::time::Duration; -use uuid::Uuid; - -/// Identity and basic status information about this sled agent -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct Inventory { - pub sled_id: SledUuid, - pub sled_agent_address: SocketAddrV6, - pub sled_role: SledRole, - pub baseboard: Baseboard, - pub usable_hardware_threads: u32, - pub usable_physical_ram: ByteCount, - pub cpu_family: SledCpuFamily, - pub reservoir_size: ByteCount, - pub disks: Vec, - pub zpools: Vec, - pub datasets: Vec, - pub ledgered_sled_config: Option, - pub reconciler_status: ConfigReconcilerInventoryStatus, - pub last_reconciliation: Option, - pub zone_image_resolver: ZoneImageResolverInventory, -} - -impl TryFrom for Inventory { - type Error = external::Error; - - fn try_from(value: inventory::Inventory) -> Result { - let inventory::Inventory { - sled_id, - sled_agent_address, - sled_role, - baseboard, - usable_hardware_threads, - usable_physical_ram, - cpu_family, - reservoir_size, - disks, - zpools, - datasets, - ledgered_sled_config, - reconciler_status, - last_reconciliation, - zone_image_resolver, - } = value; - let ledgered_sled_config = - ledgered_sled_config.map(TryInto::try_into).transpose()?; - let reconciler_status = reconciler_status.try_into()?; - let last_reconciliation = - last_reconciliation.map(TryInto::try_into).transpose()?; - Ok(Self { - sled_id, - sled_agent_address, - sled_role, - baseboard, - usable_hardware_threads, - usable_physical_ram, - cpu_family, - reservoir_size, - disks, - zpools, - datasets, - ledgered_sled_config, - reconciler_status, - last_reconciliation, - zone_image_resolver, - }) - } -} - -/// Describes the set of Reconfigurator-managed configuration elements of a sled -#[derive(Clone, Debug, Deserialize, Eq, Serialize, JsonSchema, PartialEq)] -pub struct OmicronSledConfig { - pub generation: Generation, - #[serde( - with = "iddqd::id_ord_map::IdOrdMapAsMap::" - )] - pub disks: IdOrdMap, - #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] - pub datasets: IdOrdMap, - #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] - pub zones: IdOrdMap, - pub remove_mupdate_override: Option, - #[serde(default = "HostPhase2DesiredSlots::current_contents")] - pub host_phase_2: HostPhase2DesiredSlots, -} - -impl TryFrom for inventory::OmicronSledConfig { - type Error = external::Error; - - fn try_from(value: OmicronSledConfig) -> Result { - let zones = value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { - generation: value.generation, - disks: value.disks, - datasets: value.datasets, - zones, - remove_mupdate_override: value.remove_mupdate_override, - host_phase_2: value.host_phase_2, - }) - } -} - -impl TryFrom for OmicronSledConfig { - type Error = external::Error; - - fn try_from( - value: inventory::OmicronSledConfig, - ) -> Result { - let zones = value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { - generation: value.generation, - disks: value.disks, - datasets: value.datasets, - zones, - remove_mupdate_override: value.remove_mupdate_override, - host_phase_2: value.host_phase_2, - }) - } -} - -/// Describes one Omicron-managed zone running on a sled -#[derive( - Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, -)] -pub struct OmicronZoneConfig { - pub id: OmicronZoneUuid, - - /// The pool on which we'll place this zone's root filesystem. - /// - /// Note that the root filesystem is transient -- the sled agent is - /// permitted to destroy this dataset each time the zone is initialized. - pub filesystem_pool: Option, - pub zone_type: OmicronZoneType, - // Use `InstallDataset` if this field is not present in a deserialized - // blueprint or ledger. - #[serde(default = "OmicronZoneImageSource::deserialize_default")] - pub image_source: OmicronZoneImageSource, -} - -impl IdOrdItem for OmicronZoneConfig { - type Key<'a> = OmicronZoneUuid; - - fn key(&self) -> Self::Key<'_> { - self.id - } - - id_upcast!(); -} - -impl TryFrom for inventory::OmicronZoneConfig { - type Error = external::Error; - - fn try_from(value: OmicronZoneConfig) -> Result { - Ok(Self { - id: value.id, - filesystem_pool: value.filesystem_pool, - zone_type: value.zone_type.try_into()?, - image_source: value.image_source, - }) - } -} - -impl TryFrom for OmicronZoneConfig { - type Error = external::Error; - - fn try_from( - value: inventory::OmicronZoneConfig, - ) -> Result { - Ok(Self { - id: value.id, - filesystem_pool: value.filesystem_pool, - zone_type: value.zone_type.try_into()?, - image_source: value.image_source, - }) - } -} - -/// Describes the set of Omicron-managed zones running on a sled -#[derive(Deserialize, Serialize, JsonSchema)] -pub struct OmicronZonesConfig { - /// generation number of this configuration - /// - /// This generation number is owned by the control plane (i.e., rss or - /// nexus, depending on whether rss-to-nexus handoff has happened). it - /// should not be bumped within sled agent. - /// - /// Sled Agent rejects attempts to set the configuration to a generation - /// older than the one it's currently running. - pub generation: Generation, - - /// list of running zones - pub zones: Vec, -} - -impl TryFrom for inventory::OmicronZonesConfig { - type Error = external::Error; - - fn try_from(value: OmicronZonesConfig) -> Result { - value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>() - .map(|zones| inventory::OmicronZonesConfig { - generation: value.generation, - zones, - }) - } -} - -impl TryFrom for OmicronZonesConfig { - type Error = external::Error; - - fn try_from( - value: inventory::OmicronZonesConfig, - ) -> Result { - value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>() - .map(|zones| OmicronZonesConfig { - generation: value.generation, - zones, - }) - } -} - -/// Describes what kind of zone this is (i.e., what component is running in it) -/// as well as any type-specific configuration -#[derive( - Clone, Debug, Deserialize, Eq, Serialize, JsonSchema, PartialEq, Hash, -)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum OmicronZoneType { - BoundaryNtp { - address: SocketAddrV6, - ntp_servers: Vec, - dns_servers: Vec, - domain: Option, - /// The service vNIC providing outbound connectivity using OPTE. - nic: NetworkInterface, - /// The SNAT configuration for outbound connections. - snat_cfg: SourceNatConfig, - }, - - /// Type of clickhouse zone used for a single node clickhouse deployment - Clickhouse { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - /// A zone used to run a Clickhouse Keeper node - /// - /// Keepers are only used in replicated clickhouse setups - ClickhouseKeeper { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - /// A zone used to run a Clickhouse Server in a replicated deployment - ClickhouseServer { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - CockroachDb { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - Crucible { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - CruciblePantry { - address: SocketAddrV6, - }, - ExternalDns { - dataset: OmicronZoneDataset, - /// The address at which the external DNS server API is reachable. - http_address: SocketAddrV6, - /// The address at which the external DNS server is reachable. - dns_address: SocketAddr, - /// The service vNIC providing external connectivity using OPTE. - nic: NetworkInterface, - }, - InternalDns { - dataset: OmicronZoneDataset, - http_address: SocketAddrV6, - dns_address: SocketAddrV6, - /// The addresses in the global zone which should be created - /// - /// For the DNS service, which exists outside the sleds's typical subnet - /// - adding an address in the GZ is necessary to allow inter-zone - /// traffic routing. - gz_address: Ipv6Addr, - - /// The address is also identified with an auxiliary bit of information - /// to ensure that the created global zone address can have a unique - /// name. - gz_address_index: u32, - }, - InternalNtp { - address: SocketAddrV6, - }, - Nexus { - /// The address at which the internal nexus server is reachable. - internal_address: SocketAddrV6, - /// The port at which the internal lockstep server is reachable. This - /// shares the same IP address with `internal_address`. - #[serde(default = "default_nexus_lockstep_port")] - lockstep_port: u16, - /// The address at which the external nexus server is reachable. - external_ip: IpAddr, - /// The service vNIC providing external connectivity using OPTE. - nic: NetworkInterface, - /// Whether Nexus's external endpoint should use TLS - external_tls: bool, - /// External DNS servers Nexus can use to resolve external hosts. - external_dns_servers: Vec, - }, - Oximeter { - address: SocketAddrV6, - }, -} - -const fn default_nexus_lockstep_port() -> u16 { - omicron_common::address::NEXUS_LOCKSTEP_PORT -} - -impl TryFrom for inventory::OmicronZoneType { - type Error = external::Error; - - fn try_from(value: OmicronZoneType) -> Result { - match value { - OmicronZoneType::BoundaryNtp { - address, - ntp_servers, - dns_servers, - domain, - nic, - snat_cfg, - } => { - let (first_port, last_port) = snat_cfg.port_range_raw(); - let snat_cfg = SourceNatConfigGeneric::new( - snat_cfg.ip, - first_port, - last_port, - ) - .map_err(|e| external::Error::invalid_request(e.to_string()))?; - Ok(Self::BoundaryNtp { - address, - ntp_servers, - dns_servers, - domain, - nic, - snat_cfg, - }) - } - OmicronZoneType::Clickhouse { address, dataset } => { - Ok(Self::Clickhouse { address, dataset }) - } - OmicronZoneType::ClickhouseKeeper { address, dataset } => { - Ok(Self::ClickhouseKeeper { address, dataset }) - } - OmicronZoneType::ClickhouseServer { address, dataset } => { - Ok(Self::ClickhouseServer { address, dataset }) - } - OmicronZoneType::CockroachDb { address, dataset } => { - Ok(Self::CockroachDb { address, dataset }) - } - OmicronZoneType::Crucible { address, dataset } => { - Ok(Self::Crucible { address, dataset }) - } - OmicronZoneType::CruciblePantry { address } => { - Ok(Self::CruciblePantry { address }) - } - OmicronZoneType::ExternalDns { - dataset, - http_address, - dns_address, - nic, - } => Ok(Self::ExternalDns { - dataset, - http_address, - dns_address, - nic, - }), - OmicronZoneType::InternalDns { - dataset, - http_address, - dns_address, - gz_address, - gz_address_index, - } => Ok(Self::InternalDns { - dataset, - http_address, - dns_address, - gz_address, - gz_address_index, - }), - OmicronZoneType::InternalNtp { address } => { - Ok(Self::InternalNtp { address }) - } - OmicronZoneType::Nexus { - internal_address, - lockstep_port, - external_ip, - nic, - external_tls, - external_dns_servers, - } => Ok(Self::Nexus { - internal_address, - lockstep_port, - external_ip, - nic, - external_tls, - external_dns_servers, - }), - OmicronZoneType::Oximeter { address } => { - Ok(Self::Oximeter { address }) - } - } - } -} - -impl TryFrom for OmicronZoneType { - type Error = external::Error; - - fn try_from( - value: inventory::OmicronZoneType, - ) -> Result { - match value { - inventory::OmicronZoneType::BoundaryNtp { - address, - ntp_servers, - dns_servers, - domain, - nic, - snat_cfg, - } => { - let snat_cfg = - SourceNatConfig::try_from(snat_cfg).map_err(|e| { - external::Error::invalid_request(e.to_string()) - })?; - Ok(Self::BoundaryNtp { - address, - ntp_servers, - dns_servers, - domain, - nic, - snat_cfg, - }) - } - inventory::OmicronZoneType::Clickhouse { address, dataset } => { - Ok(Self::Clickhouse { address, dataset }) - } - inventory::OmicronZoneType::ClickhouseKeeper { - address, - dataset, - } => Ok(Self::ClickhouseKeeper { address, dataset }), - inventory::OmicronZoneType::ClickhouseServer { - address, - dataset, - } => Ok(Self::ClickhouseServer { address, dataset }), - inventory::OmicronZoneType::CockroachDb { address, dataset } => { - Ok(Self::CockroachDb { address, dataset }) - } - inventory::OmicronZoneType::Crucible { address, dataset } => { - Ok(Self::Crucible { address, dataset }) - } - inventory::OmicronZoneType::CruciblePantry { address } => { - Ok(Self::CruciblePantry { address }) - } - inventory::OmicronZoneType::ExternalDns { - dataset, - http_address, - dns_address, - nic, - } => Ok(Self::ExternalDns { - dataset, - http_address, - dns_address, - nic, - }), - inventory::OmicronZoneType::InternalDns { - dataset, - http_address, - dns_address, - gz_address, - gz_address_index, - } => Ok(Self::InternalDns { - dataset, - http_address, - dns_address, - gz_address, - gz_address_index, - }), - inventory::OmicronZoneType::InternalNtp { address } => { - Ok(Self::InternalNtp { address }) - } - inventory::OmicronZoneType::Nexus { - internal_address, - lockstep_port, - external_ip, - nic, - external_tls, - external_dns_servers, - } => Ok(Self::Nexus { - internal_address, - lockstep_port, - external_ip, - nic, - external_tls, - external_dns_servers, - }), - inventory::OmicronZoneType::Oximeter { address } => { - Ok(Self::Oximeter { address }) - } - } - } -} - -/// Describes the last attempt made by the sled-agent-config-reconciler to -/// reconcile the current sled config against the actual state of the sled. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct ConfigReconcilerInventory { - pub last_reconciled_config: OmicronSledConfig, - pub external_disks: - BTreeMap, - pub datasets: BTreeMap, - pub orphaned_datasets: IdOrdMap, - pub zones: BTreeMap, - pub boot_partitions: BootPartitionContents, - /// The result of removing the mupdate override file on disk. - /// - /// `None` if `remove_mupdate_override` was not provided in the sled config. - pub remove_mupdate_override: Option, -} - -impl TryFrom - for inventory::ConfigReconcilerInventory -{ - type Error = external::Error; - - fn try_from(value: ConfigReconcilerInventory) -> Result { - Ok(Self { - last_reconciled_config: value.last_reconciled_config.try_into()?, - external_disks: value.external_disks, - datasets: value.datasets, - orphaned_datasets: value.orphaned_datasets, - zones: value.zones, - boot_partitions: value.boot_partitions, - remove_mupdate_override: value.remove_mupdate_override, - }) - } -} - -impl TryFrom - for ConfigReconcilerInventory -{ - type Error = external::Error; - - fn try_from( - value: inventory::ConfigReconcilerInventory, - ) -> Result { - Ok(Self { - last_reconciled_config: value.last_reconciled_config.try_into()?, - external_disks: value.external_disks, - datasets: value.datasets, - orphaned_datasets: value.orphaned_datasets, - zones: value.zones, - boot_partitions: value.boot_partitions, - remove_mupdate_override: value.remove_mupdate_override, - }) - } -} - -/// Status of the sled-agent-config-reconciler task. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] -#[serde(tag = "status", rename_all = "snake_case")] -pub enum ConfigReconcilerInventoryStatus { - /// The reconciler task has not yet run for the first time since sled-agent - /// started. - NotYetRun, - /// The reconciler task is actively running. - Running { - config: Box, - started_at: DateTime, - running_for: Duration, - }, - /// The reconciler task is currently idle, but previously did complete a - /// reconciliation attempt. - /// - /// This variant does not include the `OmicronSledConfig` used in the last - /// attempt, because that's always available via - /// [`ConfigReconcilerInventory::last_reconciled_config`]. - Idle { completed_at: DateTime, ran_for: Duration }, -} - -impl TryFrom - for inventory::ConfigReconcilerInventoryStatus -{ - type Error = external::Error; - - fn try_from( - value: ConfigReconcilerInventoryStatus, - ) -> Result { - match value { - ConfigReconcilerInventoryStatus::NotYetRun => Ok(Self::NotYetRun), - ConfigReconcilerInventoryStatus::Running { - config, - started_at, - running_for, - } => Ok(Self::Running { - config: Box::new((*config).try_into()?), - started_at, - running_for, - }), - ConfigReconcilerInventoryStatus::Idle { completed_at, ran_for } => { - Ok(Self::Idle { completed_at, ran_for }) - } - } - } -} - -impl TryFrom - for ConfigReconcilerInventoryStatus -{ - type Error = external::Error; - - fn try_from( - value: inventory::ConfigReconcilerInventoryStatus, - ) -> Result { - match value { - inventory::ConfigReconcilerInventoryStatus::NotYetRun => { - Ok(Self::NotYetRun) - } - inventory::ConfigReconcilerInventoryStatus::Running { - config, - started_at, - running_for, - } => Ok(Self::Running { - config: Box::new((*config).try_into()?), - started_at, - running_for, - }), - inventory::ConfigReconcilerInventoryStatus::Idle { - completed_at, - ran_for, - } => Ok(Self::Idle { completed_at, ran_for }), - } - } -} - -/// The body of a request to ensure that a instance and VMM are known to a sled -/// agent. -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct InstanceEnsureBody { - /// The virtual hardware configuration this virtual machine should have when - /// it is started. - pub vmm_spec: VmmSpec, - - /// Information about the sled-local configuration that needs to be - /// established to make the VM's virtual hardware fully functional. - pub local_config: InstanceSledLocalConfig, - - /// The initial VMM runtime state for the VMM being registered. - pub vmm_runtime: VmmRuntimeState, - - /// The ID of the instance for which this VMM is being created. - pub instance_id: InstanceUuid, - - /// The ID of the migration in to this VMM, if this VMM is being - /// ensured is part of a migration in. If this is `None`, the VMM is not - /// being created due to a migration. - pub migration_id: Option, - - /// The address at which this VMM should serve a Propolis server API. - pub propolis_addr: SocketAddr, - - /// Metadata used to track instance statistics. - pub metadata: InstanceMetadata, -} - -impl TryFrom for crate::instance::InstanceEnsureBody { - type Error = external::Error; - - fn try_from(value: InstanceEnsureBody) -> Result { - let InstanceEnsureBody { - vmm_spec, - local_config, - vmm_runtime, - instance_id, - migration_id, - propolis_addr, - metadata, - } = value; - let local_config = local_config.try_into()?; - Ok(Self { - vmm_spec, - local_config, - vmm_runtime, - instance_id, - migration_id, - propolis_addr, - metadata, - }) - } -} - -/// Describes sled-local configuration that a sled-agent must establish to make -/// the instance's virtual hardware fully functional. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct InstanceSledLocalConfig { - pub hostname: Hostname, - pub nics: Vec, - pub source_nat: SourceNatConfig, - /// Zero or more external IP addresses (either floating or ephemeral), - /// provided to an instance to allow inbound connectivity. - pub ephemeral_ip: Option, - pub floating_ips: Vec, - pub multicast_groups: Vec, - pub firewall_rules: Vec, - pub dhcp_config: DhcpConfig, - pub delegated_zvols: Vec, -} - -impl TryFrom - for crate::instance::InstanceSledLocalConfig -{ - type Error = external::Error; - - fn try_from(value: InstanceSledLocalConfig) -> Result { - let InstanceSledLocalConfig { - hostname, - nics, - source_nat, - ephemeral_ip, - floating_ips, - multicast_groups, - firewall_rules, - dhcp_config, - delegated_zvols, - } = value; - let external_ips = ExternalIpConfig::try_from_generic( - Some(source_nat), - ephemeral_ip, - floating_ips, - ) - .map_err(|e| external::Error::invalid_request(e.to_string()))?; - // NOTE: Previous versions always had the source NAT information - // specified, it wasn't optional. The newer version added support for - // optional SNAT addresses. - let external_ips = Some(external_ips); - Ok(Self { - hostname, - nics, - external_ips, - multicast_groups, - firewall_rules, - dhcp_config, - delegated_zvols, - }) - } -} diff --git a/sled-agent/types/src/lib.rs b/sled-agent/types/src/lib.rs index 8ea8bd70e9e..ada7793b041 100644 --- a/sled-agent/types/src/lib.rs +++ b/sled-agent/types/src/lib.rs @@ -4,8 +4,12 @@ //! Common types for sled-agent. +pub mod artifact; pub mod boot_disk; pub mod bootstore; +pub mod dataset; +pub mod debug; +pub mod diagnostics; pub mod disk; pub mod early_networking; pub mod firewall_rules; diff --git a/sled-agent/types/src/probes.rs b/sled-agent/types/src/probes.rs new file mode 100644 index 00000000000..3455462cca7 --- /dev/null +++ b/sled-agent/types/src/probes.rs @@ -0,0 +1,7 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Types for manipulating networking probe zones. + +pub use sled_agent_types_versions::latest::probes::*; diff --git a/sled-agent/types/src/probes/v1.rs b/sled-agent/types/src/probes/v1.rs deleted file mode 100644 index 38284e6f7d8..00000000000 --- a/sled-agent/types/src/probes/v1.rs +++ /dev/null @@ -1,72 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -//! Version 1 of types for manipulating networking probe zones. - -use iddqd::IdHashItem; -use iddqd::IdHashMap; -use iddqd::id_upcast; -use omicron_common::api::external; -use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; -use omicron_uuid_kinds::ProbeUuid; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; - -// Re-export types that haven't changed. -pub use super::ExternalIp; -pub use super::IpKind; - -/// Parameters used to create a probe. -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct ProbeCreate { - /// The ID for the probe. - pub id: ProbeUuid, - /// The external IP addresses assigned to the probe. - pub external_ips: Vec, - /// The probe's networking interface. - pub interface: NetworkInterface, -} - -impl IdHashItem for ProbeCreate { - type Key<'a> = ProbeUuid; - - fn key(&self) -> Self::Key<'_> { - self.id - } - - id_upcast!(); -} - -impl TryFrom for super::ProbeCreate { - type Error = external::Error; - - fn try_from(value: ProbeCreate) -> Result { - value.interface.try_into().map(|interface| Self { - id: value.id, - external_ips: value.external_ips, - interface, - }) - } -} - -/// A set of probes that the target sled should run. -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct ProbeSet { - /// The exact set of probes to run. - pub probes: IdHashMap, -} - -impl TryFrom for super::ProbeSet { - type Error = external::Error; - - fn try_from(value: ProbeSet) -> Result { - value - .probes - .into_iter() - .map(TryInto::try_into) - .collect::>() - .map(|probes| Self { probes }) - } -} diff --git a/sled-agent/types/src/rack_init.rs b/sled-agent/types/src/rack_init.rs index cb2e2547271..499e2c647ad 100644 --- a/sled-agent/types/src/rack_init.rs +++ b/sled-agent/types/src/rack_init.rs @@ -11,7 +11,6 @@ use std::{ use anyhow::{Result, bail}; use camino::{Utf8Path, Utf8PathBuf}; -pub use nexus_sled_agent_shared::recovery_silo::RecoverySiloConfig; use omicron_common::{ address::{ AZ_PREFIX, IpRange, Ipv6Subnet, RACK_PREFIX, SLED_PREFIX, get_64_subnet, @@ -23,6 +22,7 @@ use omicron_common::{ }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +pub use sled_agent_types_versions::latest::rack_init::*; use sled_hardware_types::Baseboard; /// Structures and routines used to maintain backwards compatibility. The diff --git a/sled-agent/types/src/sled.rs b/sled-agent/types/src/sled.rs index bfd576e3af7..0dd099e1a4b 100644 --- a/sled-agent/types/src/sled.rs +++ b/sled-agent/types/src/sled.rs @@ -4,264 +4,6 @@ //! Types related to operating on sleds. -use std::net::{IpAddr, Ipv6Addr, SocketAddrV6}; - -use async_trait::async_trait; -use daft::Diffable; -use omicron_common::{ - address::{self, Ipv6Subnet, SLED_PREFIX}, - ledger::Ledgerable, -}; -use omicron_uuid_kinds::SledUuid; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use sha3::{Digest, Sha3_256}; -use uuid::Uuid; +pub use sled_agent_types_versions::latest::sled::*; pub const SWITCH_ZONE_BASEBOARD_FILE: &str = "/opt/oxide/baseboard.json"; - -/// A representation of a Baseboard ID as used in the inventory subsystem -/// This type is essentially the same as a `Baseboard` except it doesn't have a -/// revision or HW type (Gimlet, PC, Unknown). -#[derive( - Clone, - Debug, - Serialize, - Deserialize, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - JsonSchema, - Diffable, -)] -#[daft(leaf)] -pub struct BaseboardId { - /// Oxide Part Number - pub part_number: String, - /// Serial number (unique for a given part number) - pub serial_number: String, -} - -impl std::fmt::Display for BaseboardId { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}:{}", self.part_number, self.serial_number) - } -} - -#[derive(Debug, thiserror::Error)] -#[error("Baseboard is of unknown type")] -pub struct UnknownBaseboardError; - -impl TryFrom for BaseboardId { - type Error = UnknownBaseboardError; - - fn try_from( - value: sled_hardware_types::Baseboard, - ) -> Result { - use sled_hardware_types::Baseboard; - match value { - Baseboard::Gimlet { identifier, model, .. } => Ok(BaseboardId { - part_number: model, - serial_number: identifier, - }), - Baseboard::Pc { identifier, model } => Ok(BaseboardId { - part_number: model, - serial_number: identifier, - }), - Baseboard::Unknown => Err(UnknownBaseboardError), - } - } -} - -/// A request to Add a given sled after rack initialization has occurred -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct AddSledRequest { - pub sled_id: BaseboardId, - pub start_request: StartSledAgentRequest, -} - -/// Configuration information for launching a Sled Agent. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct StartSledAgentRequest { - /// The current generation number of data as stored in CRDB. - /// - /// The initial generation is set during RSS time and then only mutated - /// by Nexus. For now, we don't actually anticipate mutating this data, - /// but we leave open the possiblity. - pub generation: u64, - - // Which version of the data structure do we have. This is to help with - // deserialization and conversion in future updates. - pub schema_version: u32, - - // The actual configuration details - pub body: StartSledAgentRequestBody, -} - -impl StartSledAgentRequest { - pub fn sled_address(&self) -> SocketAddrV6 { - address::get_sled_address(self.body.subnet) - } - - pub fn switch_zone_ip(&self) -> Ipv6Addr { - address::get_switch_zone_address(self.body.subnet) - } - - /// Compute the sha3_256 digest of `self.rack_id` to use as a `salt` - /// for disk encryption. We don't want to include other values that are - /// consistent across sleds as it would prevent us from moving drives - /// between sleds. - pub fn hash_rack_id(&self) -> [u8; 32] { - // We know the unwrap succeeds as a Sha3_256 digest is 32 bytes - Sha3_256::digest(self.body.rack_id.as_bytes()) - .as_slice() - .try_into() - .unwrap() - } -} - -#[async_trait] -impl Ledgerable for StartSledAgentRequest { - fn is_newer_than(&self, other: &Self) -> bool { - self.generation > other.generation - } - - fn generation_bump(&mut self) { - // DO NOTHING! - // - // Generation bumps must only ever come from nexus and will be encoded - // in the struct itself - } - - // Attempt to deserialize the v1 or v0 version and return - // the v1 version. - fn deserialize( - s: &str, - ) -> Result { - // Try to deserialize the latest version of the data structure (v1). If - // that succeeds we are done. - if let Ok(val) = serde_json::from_str::(s) { - return Ok(val); - } - - // We don't have the latest version. Try to deserialize v0 and then - // convert it to the latest version. - let v0 = serde_json::from_str::(s)?.request; - Ok(v0.into()) - } -} - -/// This is the actual app level data of `StartSledAgentRequest` -/// -/// We nest it below the "header" of `generation` and `schema_version` so that -/// we can perform partial deserialization of `EarlyNetworkConfig` to only read -/// the header and defer deserialization of the body once we know the schema -/// version. This is possible via the use of [`serde_json::value::RawValue`] in -/// future (post-v1) deserialization paths. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct StartSledAgentRequestBody { - /// Uuid of the Sled Agent to be created. - pub id: SledUuid, - - /// Uuid of the rack to which this sled agent belongs. - pub rack_id: Uuid, - - /// Use trust quorum for key generation - pub use_trust_quorum: bool, - - /// Is this node an LRTQ learner node? - /// - /// We only put the node into learner mode if `use_trust_quorum` is also - /// true. - pub is_lrtq_learner: bool, - - /// Portion of the IP space to be managed by the Sled Agent. - pub subnet: Ipv6Subnet, -} - -/// The version of `StartSledAgentRequest` we originally shipped with. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct StartSledAgentRequestV0 { - /// Uuid of the Sled Agent to be created. - pub id: SledUuid, - - /// Uuid of the rack to which this sled agent belongs. - pub rack_id: Uuid, - - /// The external NTP servers to use - pub ntp_servers: Vec, - - /// The external DNS servers to use - pub dns_servers: Vec, - - /// Use trust quorum for key generation - pub use_trust_quorum: bool, - - // Note: The order of these fields is load bearing, because we serialize - // `SledAgentRequest`s as toml. `subnet` serializes as a TOML table, so it - // must come after non-table fields. - /// Portion of the IP space to be managed by the Sled Agent. - pub subnet: Ipv6Subnet, -} - -impl From for StartSledAgentRequest { - fn from(v0: StartSledAgentRequestV0) -> Self { - StartSledAgentRequest { - generation: 0, - schema_version: 1, - body: StartSledAgentRequestBody { - id: v0.id, - rack_id: v0.rack_id, - use_trust_quorum: v0.use_trust_quorum, - is_lrtq_learner: false, - subnet: v0.subnet, - }, - } - } -} - -// A wrapper around StartSledAgentRequestV0 that was used -// for the ledger format. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -struct PersistentSledAgentRequest { - request: StartSledAgentRequestV0, -} - -#[cfg(test)] -mod tests { - use std::net::Ipv6Addr; - - use super::*; - - #[test] - fn serialize_start_sled_agent_v0_deserialize_v1() { - let v0 = PersistentSledAgentRequest { - request: StartSledAgentRequestV0 { - id: SledUuid::new_v4(), - rack_id: Uuid::new_v4(), - ntp_servers: vec![String::from("test.pool.example.com")], - dns_servers: vec!["1.1.1.1".parse().unwrap()], - use_trust_quorum: false, - subnet: Ipv6Subnet::new(Ipv6Addr::LOCALHOST), - }, - }; - let serialized = serde_json::to_string(&v0).unwrap(); - let expected = StartSledAgentRequest { - generation: 0, - schema_version: 1, - body: StartSledAgentRequestBody { - id: v0.request.id, - rack_id: v0.request.rack_id, - use_trust_quorum: v0.request.use_trust_quorum, - is_lrtq_learner: false, - subnet: v0.request.subnet, - }, - }; - - let actual: StartSledAgentRequest = - Ledgerable::deserialize(&serialized).unwrap(); - assert_eq!(expected, actual); - } -} diff --git a/sled-agent/types/src/support_bundle.rs b/sled-agent/types/src/support_bundle.rs index 42fff3374bc..40819645c7e 100644 --- a/sled-agent/types/src/support_bundle.rs +++ b/sled-agent/types/src/support_bundle.rs @@ -4,6 +4,8 @@ //! Types related to support bundles. +pub use sled_agent_types_versions::latest::support_bundle::*; + // The final name of the bundle, as it is stored within the dedicated // datasets. // diff --git a/sled-agent/types/src/zone_bundle.rs b/sled-agent/types/src/zone_bundle.rs index 3766eb2d748..d89e562ab95 100644 --- a/sled-agent/types/src/zone_bundle.rs +++ b/sled-agent/types/src/zone_bundle.rs @@ -4,265 +4,16 @@ //! Types related to zone bundles. -use std::{cmp::Ordering, collections::HashSet, time::Duration}; +use std::cmp::Ordering; use camino::Utf8PathBuf; -use chrono::{DateTime, Utc}; -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use uuid::Uuid; -/// An identifier for a zone bundle. -#[derive( - Clone, - Debug, - Deserialize, - Eq, - Hash, - JsonSchema, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -pub struct ZoneBundleId { - /// The name of the zone this bundle is derived from. - pub zone_name: String, - /// The ID for this bundle itself. - pub bundle_id: Uuid, -} - -/// The reason or cause for a zone bundle, i.e., why it was created. -// -// NOTE: The ordering of the enum variants is important, and should not be -// changed without careful consideration. -// -// The ordering is used when deciding which bundles to remove automatically. In -// addition to time, the cause is used to sort bundles, so changing the variant -// order will change that priority. -#[derive( - Clone, - Copy, - Debug, - Default, - Deserialize, - Eq, - Hash, - JsonSchema, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -#[serde(rename_all = "snake_case")] -#[non_exhaustive] -pub enum ZoneBundleCause { - /// Some other, unspecified reason. - #[default] - Other, - /// A zone bundle taken when a sled agent finds a zone that it does not - /// expect to be running. - UnexpectedZone, - /// An instance zone was terminated. - TerminatedInstance, -} +pub use sled_agent_types_versions::latest::zone_bundle::*; -/// Metadata about a zone bundle. -#[derive( - Clone, - Debug, - Deserialize, - Eq, - Hash, - JsonSchema, - Ord, - PartialEq, - PartialOrd, - Serialize, -)] -pub struct ZoneBundleMetadata { - /// Identifier for this zone bundle - pub id: ZoneBundleId, - /// The time at which this zone bundle was created. - pub time_created: DateTime, - /// A version number for this zone bundle. - pub version: u8, - /// The reason or cause a bundle was created. - pub cause: ZoneBundleCause, -} - -impl ZoneBundleMetadata { - pub const VERSION: u8 = 0; - - /// Create a new set of metadata for the provided zone. - pub fn new(zone_name: &str, cause: ZoneBundleCause) -> Self { - Self { - id: ZoneBundleId { - zone_name: zone_name.to_string(), - bundle_id: Uuid::new_v4(), - }, - time_created: Utc::now(), - version: Self::VERSION, - cause, - } - } -} - -/// A dimension along with bundles can be sorted, to determine priority. -#[derive( - Clone, - Copy, - Debug, - Deserialize, - Eq, - Hash, - JsonSchema, - Serialize, - Ord, - PartialEq, - PartialOrd, -)] -#[serde(rename_all = "snake_case")] -pub enum PriorityDimension { - /// Sorting by time, with older bundles with lower priority. - Time, - /// Sorting by the cause for creating the bundle. - Cause, - // TODO-completeness: Support zone or zone type (e.g., service vs instance)? -} - -/// The priority order for bundles during cleanup. +/// Information about a zone bundle. /// -/// Bundles are sorted along the dimensions in [`PriorityDimension`], with each -/// dimension appearing exactly once. During cleanup, lesser-priority bundles -/// are pruned first, to maintain the dataset quota. Note that bundles are -/// sorted by each dimension in the order in which they appear, with each -/// dimension having higher priority than the next. -#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] -pub struct PriorityOrder([PriorityDimension; PriorityOrder::EXPECTED_SIZE]); - -impl std::ops::Deref for PriorityOrder { - type Target = [PriorityDimension; PriorityOrder::EXPECTED_SIZE]; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Default for PriorityOrder { - fn default() -> Self { - Self::DEFAULT - } -} - -impl PriorityOrder { - // NOTE: Must match the number of variants in `PriorityDimension`. - const EXPECTED_SIZE: usize = 2; - const DEFAULT: Self = - Self([PriorityDimension::Cause, PriorityDimension::Time]); - - /// Construct a new priority order. - /// - /// This requires that each dimension appear exactly once. - pub fn new( - dims: &[PriorityDimension], - ) -> Result { - if dims.len() != Self::EXPECTED_SIZE { - return Err(PriorityOrderCreateError::WrongDimensionCount( - dims.len(), - )); - } - let mut seen = HashSet::new(); - for dim in dims.iter() { - if !seen.insert(dim) { - return Err(PriorityOrderCreateError::DuplicateFound(*dim)); - } - } - Ok(Self(dims.try_into().unwrap())) - } - - /// Get the priority order as a slice. - pub fn as_slice(&self) -> &[PriorityDimension] { - &self.0 - } - - /// Order zone bundle info according to the contained priority. - /// - /// We sort the info by each dimension, in the order in which it appears. - /// That means earlier dimensions have higher priority than later ones. - pub fn compare_bundles( - &self, - lhs: &ZoneBundleInfo, - rhs: &ZoneBundleInfo, - ) -> Ordering { - for dim in self.0.iter() { - let ord = match dim { - PriorityDimension::Cause => { - lhs.metadata.cause.cmp(&rhs.metadata.cause) - } - PriorityDimension::Time => { - lhs.metadata.time_created.cmp(&rhs.metadata.time_created) - } - }; - if matches!(ord, Ordering::Equal) { - continue; - } - return ord; - } - Ordering::Equal - } -} - -/// A period on which bundles are automatically cleaned up. -#[derive( - Clone, Copy, Deserialize, JsonSchema, PartialEq, PartialOrd, Serialize, -)] -pub struct CleanupPeriod(Duration); - -impl Default for CleanupPeriod { - fn default() -> Self { - Self(Duration::from_secs(600)) - } -} - -impl CleanupPeriod { - /// The minimum supported cleanup period. - pub const MIN: Self = Self(Duration::from_secs(60)); - - /// The maximum supported cleanup period. - pub const MAX: Self = Self(Duration::from_secs(60 * 60 * 24)); - - /// Construct a new cleanup period, checking that it's valid. - pub fn new(duration: Duration) -> Result { - if duration >= Self::MIN.as_duration() - && duration <= Self::MAX.as_duration() - { - Ok(Self(duration)) - } else { - Err(CleanupPeriodCreateError::OutOfBounds(duration)) - } - } - - /// Return the period as a duration. - pub const fn as_duration(&self) -> Duration { - self.0 - } -} - -impl TryFrom for CleanupPeriod { - type Error = CleanupPeriodCreateError; - - fn try_from(duration: Duration) -> Result { - Self::new(duration) - } -} - -impl std::fmt::Debug for CleanupPeriod { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - self.0.fmt(f) - } -} - +/// This type is not published in the API, so it remains defined here +/// rather than in the migrations crate. #[derive(Clone, Debug, PartialEq)] pub struct ZoneBundleInfo { /// The raw metadata for the bundle @@ -273,199 +24,29 @@ pub struct ZoneBundleInfo { pub bytes: u64, } -/// The portion of a debug dataset used for zone bundles. -#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)] -pub struct BundleUtilization { - /// The total dataset quota, in bytes. - pub dataset_quota: u64, - /// The total number of bytes available for zone bundles. - /// - /// This is `dataset_quota` multiplied by the context's storage limit. - pub bytes_available: u64, - /// Total bundle usage, in bytes. - pub bytes_used: u64, -} - -/// Context provided for the zone bundle cleanup task. -#[derive( - Clone, Copy, Debug, Default, Deserialize, JsonSchema, PartialEq, Serialize, -)] -pub struct CleanupContext { - /// The period on which automatic checks and cleanup is performed. - pub period: CleanupPeriod, - /// The limit on the dataset quota available for zone bundles. - pub storage_limit: StorageLimit, - /// The priority ordering for keeping old bundles. - pub priority: PriorityOrder, -} - -/// The count of bundles / bytes removed during a cleanup operation. -#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema, Serialize)] -pub struct CleanupCount { - /// The number of bundles removed. - pub bundles: u64, - /// The number of bytes removed. - pub bytes: u64, -} - -/// The limit on space allowed for zone bundles, as a percentage of the overall -/// dataset's quota. -#[derive( - Clone, - Copy, - Debug, - Deserialize, - JsonSchema, - PartialEq, - PartialOrd, - Serialize, -)] -pub struct StorageLimit(u8); - -impl std::fmt::Display for StorageLimit { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}%", self.as_u8()) - } -} - -impl Default for StorageLimit { - fn default() -> Self { - StorageLimit(25) - } -} - -impl StorageLimit { - /// Minimum percentage of dataset quota supported. - pub const MIN: Self = Self(0); - - /// Maximum percentage of dataset quota supported. - pub const MAX: Self = Self(50); - - /// Construct a new limit allowed for zone bundles. - /// - /// This should be expressed as a percentage, in the range (Self::MIN, - /// Self::MAX]. - pub const fn new(percentage: u8) -> Result { - if percentage > Self::MIN.0 && percentage <= Self::MAX.0 { - Ok(Self(percentage)) - } else { - Err(StorageLimitCreateError::OutOfBounds(percentage)) - } - } - - /// Return the contained quota percentage. - pub const fn as_u8(&self) -> u8 { - self.0 - } - - // Compute the number of bytes available from a dataset quota, in bytes. - pub const fn bytes_available(&self, dataset_quota: u64) -> u64 { - (dataset_quota * self.as_u8() as u64) / 100 - } -} - -#[derive(Debug, Error)] -pub enum PriorityOrderCreateError { - #[error("expected exactly {n} dimensions, found {0}", n = PriorityOrder::EXPECTED_SIZE)] - WrongDimensionCount(usize), - #[error("duplicate element found in priority ordering: {0:?}")] - DuplicateFound(PriorityDimension), -} - -#[derive(Debug, Error)] -pub enum CleanupPeriodCreateError { - #[error( - "invalid cleanup period ({0:?}): must be \ - between {min:?} and {max:?}, inclusive", - min = CleanupPeriod::MIN, - max = CleanupPeriod::MAX, - )] - OutOfBounds(Duration), -} - -#[derive(Debug, Error)] -pub enum StorageLimitCreateError { - #[error("invalid storage limit ({0}): must be expressed as a percentage in ({min}, {max}]", - min = StorageLimit::MIN.0, - max = StorageLimit::MAX.0, - )] - OutOfBounds(u8), +/// Order zone bundle info according to the contained priority. +/// +/// We sort the info by each dimension, in the order in which it appears. +/// That means earlier dimensions have higher priority than later ones. +pub fn compare_bundles( + order: &PriorityOrder, + lhs: &ZoneBundleInfo, + rhs: &ZoneBundleInfo, +) -> Ordering { + order.compare_metadata(&lhs.metadata, &rhs.metadata) } #[cfg(test)] mod tests { - use chrono::TimeZone; + use chrono::{TimeZone, Utc}; use super::*; - #[test] - fn test_sort_zone_bundle_cause() { - use ZoneBundleCause::*; - let mut original = [Other, TerminatedInstance, UnexpectedZone]; - let expected = [Other, UnexpectedZone, TerminatedInstance]; - original.sort(); - assert_eq!(original, expected); - } - - #[test] - fn test_priority_dimension() { - assert!(PriorityOrder::new(&[]).is_err()); - assert!(PriorityOrder::new(&[PriorityDimension::Cause]).is_err()); - assert!( - PriorityOrder::new(&[ - PriorityDimension::Cause, - PriorityDimension::Cause - ]) - .is_err() - ); - assert!( - PriorityOrder::new(&[ - PriorityDimension::Cause, - PriorityDimension::Cause, - PriorityDimension::Time - ]) - .is_err() - ); - - assert!( - PriorityOrder::new(&[ - PriorityDimension::Cause, - PriorityDimension::Time - ]) - .is_ok() - ); - assert_eq!( - PriorityOrder::new(PriorityOrder::default().as_slice()).unwrap(), - PriorityOrder::default() - ); - } - - #[test] - fn test_storage_limit_bytes_available() { - let pct = StorageLimit(1); - assert_eq!(pct.bytes_available(100), 1); - assert_eq!(pct.bytes_available(1000), 10); - - let pct = StorageLimit(100); - assert_eq!(pct.bytes_available(100), 100); - assert_eq!(pct.bytes_available(1000), 1000); - - let pct = StorageLimit(100); - assert_eq!(pct.bytes_available(99), 99); - - let pct = StorageLimit(99); - assert_eq!(pct.bytes_available(1), 0); - - // Test non-power of 10. - let pct = StorageLimit(25); - assert_eq!(pct.bytes_available(32768), 8192); - } - #[test] fn test_compare_bundles() { use PriorityDimension::*; - let time_first = PriorityOrder([Time, Cause]); - let cause_first = PriorityOrder([Cause, Time]); + let time_first = PriorityOrder::new(&[Time, Cause]).unwrap(); + let cause_first = PriorityOrder::new(&[Cause, Time]).unwrap(); fn make_info( year: i32, @@ -499,7 +80,7 @@ mod tests { ]; let mut sorted = info.clone(); - sorted.sort_by(|lhs, rhs| time_first.compare_bundles(lhs, rhs)); + sorted.sort_by(|lhs, rhs| compare_bundles(&time_first, lhs, rhs)); // Low -> high priority // [old/unexpected, old/terminated, new/unexpected, new/terminated] let expected = [ @@ -514,7 +95,7 @@ mod tests { ); let mut sorted = info.clone(); - sorted.sort_by(|lhs, rhs| cause_first.compare_bundles(lhs, rhs)); + sorted.sort_by(|lhs, rhs| compare_bundles(&cause_first, lhs, rhs)); // Low -> high priority // [old/unexpected, new/unexpected, old/terminated, new/terminated] let expected = [ diff --git a/sled-agent/types/src/zone_images.rs b/sled-agent/types/src/zone_images.rs index d82783f811d..642be56e47a 100644 --- a/sled-agent/types/src/zone_images.rs +++ b/sled-agent/types/src/zone_images.rs @@ -6,24 +6,24 @@ use std::{fmt, fs::FileType, io, sync::Arc}; use camino::Utf8PathBuf; use iddqd::{IdOrdItem, IdOrdMap, id_upcast}; -use nexus_sled_agent_shared::inventory::MupdateOverrideBootInventory; -use nexus_sled_agent_shared::inventory::MupdateOverrideInventory; -use nexus_sled_agent_shared::inventory::MupdateOverrideNonBootInventory; -use nexus_sled_agent_shared::inventory::OmicronZoneConfig; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideBootSuccessInventory; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideInventory; -use nexus_sled_agent_shared::inventory::ZoneArtifactInventory; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; -use nexus_sled_agent_shared::inventory::ZoneKind; -use nexus_sled_agent_shared::inventory::ZoneManifestBootInventory; -use nexus_sled_agent_shared::inventory::ZoneManifestInventory; -use nexus_sled_agent_shared::inventory::ZoneManifestNonBootInventory; use omicron_common::update::{ MupdateOverrideInfo, OmicronZoneManifest, OmicronZoneManifestSource, }; use omicron_common::zone_images::ZoneImageFileSource; use omicron_uuid_kinds::InternalZpoolUuid; use omicron_uuid_kinds::MupdateOverrideUuid; +use sled_agent_types_versions::latest::inventory::MupdateOverrideBootInventory; +use sled_agent_types_versions::latest::inventory::MupdateOverrideInventory; +use sled_agent_types_versions::latest::inventory::MupdateOverrideNonBootInventory; +use sled_agent_types_versions::latest::inventory::OmicronZoneConfig; +use sled_agent_types_versions::latest::inventory::RemoveMupdateOverrideBootSuccessInventory; +use sled_agent_types_versions::latest::inventory::RemoveMupdateOverrideInventory; +use sled_agent_types_versions::latest::inventory::ZoneArtifactInventory; +use sled_agent_types_versions::latest::inventory::ZoneImageResolverInventory; +use sled_agent_types_versions::latest::inventory::ZoneKind; +use sled_agent_types_versions::latest::inventory::ZoneManifestBootInventory; +use sled_agent_types_versions::latest::inventory::ZoneManifestInventory; +use sled_agent_types_versions::latest::inventory::ZoneManifestNonBootInventory; use slog::{error, info, o, warn}; use slog_error_chain::InlineErrorChain; use swrite::{SWrite, swriteln}; diff --git a/nexus-sled-agent-shared/Cargo.toml b/sled-agent/types/versions/Cargo.toml similarity index 77% rename from nexus-sled-agent-shared/Cargo.toml rename to sled-agent/types/versions/Cargo.toml index f71c13101d9..a70565a47c0 100644 --- a/nexus-sled-agent-shared/Cargo.toml +++ b/sled-agent/types/versions/Cargo.toml @@ -1,7 +1,8 @@ [package] -name = "nexus-sled-agent-shared" +name = "sled-agent-types-versions" version = "0.1.0" edition.workspace = true +license = "MPL-2.0" [lints] workspace = true @@ -13,15 +14,20 @@ daft.workspace = true iddqd.workspace = true illumos-utils.workspace = true indent_write.workspace = true +async-trait.workspace = true +bootstore.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true -# TODO: replace uses of propolis_client with local types +oxnet.workspace = true +slog.workspace = true +propolis_api_types.workspace = true proptest = { workspace = true, optional = true } schemars.workspace = true serde.workspace = true serde_json.workspace = true +sha3.workspace = true sled-hardware-types.workspace = true strum.workspace = true test-strategy = { workspace = true, optional = true } @@ -30,6 +36,7 @@ tufaceous-artifact.workspace = true uuid.workspace = true [dev-dependencies] +omicron-test-utils.workspace = true proptest.workspace = true test-strategy.workspace = true diff --git a/sled-agent/types/versions/src/add_dual_stack_external_ip_config/firewall_rules.rs b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/firewall_rules.rs new file mode 100644 index 00000000000..ac68cc0127e --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/firewall_rules.rs @@ -0,0 +1,45 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Firewall rule types for version `ADD_DUAL_STACK_EXTERNAL_IP_CONFIG`. + +use crate::{v9, v10}; +use omicron_common::api::external; +use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Update firewall rules for a VPC +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct VpcFirewallRulesEnsureBody { + pub vni: external::Vni, + pub rules: Vec, +} + +impl TryFrom + for VpcFirewallRulesEnsureBody +{ + type Error = external::Error; + + fn try_from( + v10: v10::firewall_rules::VpcFirewallRulesEnsureBody, + ) -> Result { + Ok(Self { vni: v10.vni, rules: v10.rules }) + } +} + +impl TryFrom + for VpcFirewallRulesEnsureBody +{ + type Error = external::Error; + + fn try_from( + v9: v9::firewall_rules::VpcFirewallRulesEnsureBody, + ) -> Result { + // Chain through v10 + let v10 = + v10::firewall_rules::VpcFirewallRulesEnsureBody::try_from(v9)?; + Self::try_from(v10) + } +} diff --git a/sled-agent/types/versions/src/add_dual_stack_external_ip_config/instance.rs b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/instance.rs new file mode 100644 index 00000000000..b221caa1d3e --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/instance.rs @@ -0,0 +1,116 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::net::SocketAddr; + +use omicron_common::api::external; +use omicron_common::api::external::Hostname; +use omicron_common::api::internal::nexus::VmmRuntimeState; +use omicron_common::api::internal::shared::DelegatedZvol; +use omicron_common::api::internal::shared::DhcpConfig; +use omicron_common::api::internal::shared::ExternalIpConfig; +use omicron_common::api::internal::shared::NetworkInterface; +use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; +use omicron_uuid_kinds::InstanceUuid; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::v1::instance::InstanceMetadata; +use crate::v1::instance::VmmSpec; +use crate::v7::instance::InstanceMulticastMembership; +use crate::v10; + +/// The body of a request to ensure that a instance and VMM are known to a sled +/// agent. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct InstanceEnsureBody { + /// The virtual hardware configuration this virtual machine should have when + /// it is started. + pub vmm_spec: VmmSpec, + + /// Information about the sled-local configuration that needs to be + /// established to make the VM's virtual hardware fully functional. + pub local_config: InstanceSledLocalConfig, + + /// The initial VMM runtime state for the VMM being registered. + pub vmm_runtime: VmmRuntimeState, + + /// The ID of the instance for which this VMM is being created. + pub instance_id: InstanceUuid, + + /// The ID of the migration in to this VMM, if this VMM is being + /// ensured is part of a migration in. If this is `None`, the VMM is not + /// being created due to a migration. + pub migration_id: Option, + + /// The address at which this VMM should serve a Propolis server API. + pub propolis_addr: SocketAddr, + + /// Metadata used to track instance statistics. + pub metadata: InstanceMetadata, +} + +/// Describes sled-local configuration that a sled-agent must establish to make +/// the instance's virtual hardware fully functional. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct InstanceSledLocalConfig { + pub hostname: Hostname, + pub nics: Vec, + pub external_ips: Option, + pub multicast_groups: Vec, + pub firewall_rules: Vec, + pub dhcp_config: DhcpConfig, + pub delegated_zvols: Vec, +} + +impl TryFrom for InstanceEnsureBody { + type Error = external::Error; + + fn try_from( + v10: v10::instance::InstanceEnsureBody, + ) -> Result { + Ok(Self { + vmm_spec: v10.vmm_spec, + local_config: v10.local_config.try_into()?, + vmm_runtime: v10.vmm_runtime, + instance_id: v10.instance_id, + migration_id: v10.migration_id, + propolis_addr: v10.propolis_addr, + metadata: v10.metadata, + }) + } +} + +impl TryFrom + for InstanceSledLocalConfig +{ + type Error = external::Error; + + fn try_from( + v10: v10::instance::InstanceSledLocalConfig, + ) -> Result { + // v10.source_nat is already a v1::SourceNatConfig, so we can use it directly + let external_ips = ExternalIpConfig::try_from_generic( + Some(v10.source_nat), + v10.ephemeral_ip, + v10.floating_ips.clone(), + ) + .map_err(|e| { + external::Error::invalid_request(format!( + "invalid external IP config: {e}" + )) + })?; + + Ok(Self { + hostname: v10.hostname, + nics: v10.nics, + external_ips: Some(external_ips), + multicast_groups: v10.multicast_groups, + firewall_rules: v10.firewall_rules, + dhcp_config: v10.dhcp_config, + delegated_zvols: v10.delegated_zvols, + }) + } +} diff --git a/sled-agent/types/versions/src/add_dual_stack_external_ip_config/inventory.rs b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/inventory.rs new file mode 100644 index 00000000000..b44725452a1 --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/inventory.rs @@ -0,0 +1,1115 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::collections::BTreeMap; +use std::net::{IpAddr, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::time::Duration; + +use chrono::{DateTime, Utc}; +use iddqd::IdOrdItem; +use iddqd::IdOrdMap; +use iddqd::id_upcast; +use omicron_common::disk::{DatasetKind, DatasetName}; +use omicron_common::ledger::Ledgerable; +use omicron_common::{ + api::{ + external::{ByteCount, Generation}, + internal::shared::{NetworkInterface, SourceNatConfigGeneric}, + }, + disk::{DatasetConfig, OmicronPhysicalDiskConfig}, + zpool_name::ZpoolName, +}; +use omicron_uuid_kinds::SledUuid; +use omicron_uuid_kinds::{DatasetUuid, OmicronZoneUuid}; +use omicron_uuid_kinds::{MupdateOverrideUuid, PhysicalDiskUuid}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::v1::inventory::{ + BootPartitionContents, ConfigReconcilerInventoryResult, + HostPhase2DesiredSlots, InventoryDataset, InventoryDisk, InventoryZpool, + OmicronZoneDataset, OmicronZoneImageSource, OrphanedDataset, + RemoveMupdateOverrideBootSuccessInventory, RemoveMupdateOverrideInventory, + SledRole, ZoneImageResolverInventory, ZoneKind, +}; +use crate::v10; +use sled_hardware_types::{Baseboard, SledCpuFamily}; + +/// Identity and basic status information about this sled agent +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct Inventory { + pub sled_id: SledUuid, + pub sled_agent_address: SocketAddrV6, + pub sled_role: SledRole, + pub baseboard: Baseboard, + pub usable_hardware_threads: u32, + pub usable_physical_ram: ByteCount, + pub cpu_family: SledCpuFamily, + pub reservoir_size: ByteCount, + pub disks: Vec, + pub zpools: Vec, + pub datasets: Vec, + pub ledgered_sled_config: Option, + pub reconciler_status: ConfigReconcilerInventoryStatus, + pub last_reconciliation: Option, + pub zone_image_resolver: ZoneImageResolverInventory, +} + +/// Describes the last attempt made by the sled-agent-config-reconciler to +/// reconcile the current sled config against the actual state of the sled. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct ConfigReconcilerInventory { + pub last_reconciled_config: OmicronSledConfig, + pub external_disks: + BTreeMap, + pub datasets: BTreeMap, + pub orphaned_datasets: IdOrdMap, + pub zones: BTreeMap, + pub boot_partitions: BootPartitionContents, + /// The result of removing the mupdate override file on disk. + /// + /// `None` if `remove_mupdate_override` was not provided in the sled config. + pub remove_mupdate_override: Option, +} + +impl ConfigReconcilerInventory { + /// Iterate over all running zones as reported by the last reconciliation + /// result. + /// + /// This includes zones that are both present in `last_reconciled_config` + /// and whose status in `zones` indicates "successfully running". + pub fn running_omicron_zones( + &self, + ) -> impl Iterator { + self.zones.iter().filter_map(|(zone_id, result)| match result { + ConfigReconcilerInventoryResult::Ok => { + self.last_reconciled_config.zones.get(zone_id) + } + ConfigReconcilerInventoryResult::Err { .. } => None, + }) + } + + /// Iterate over all zones contained in the most-recently-reconciled sled + /// config and report their status as of that reconciliation. + pub fn reconciled_omicron_zones( + &self, + ) -> impl Iterator + { + // `self.zones` may contain zone IDs that aren't present in + // `last_reconciled_config` at all, if we failed to _shut down_ zones + // that are no longer present in the config. We use `filter_map` to + // strip those out, and only report on the configured zones. + self.zones.iter().filter_map(|(zone_id, result)| { + let config = self.last_reconciled_config.zones.get(zone_id)?; + Some((config, result)) + }) + } + + /// Given a sled config, produce a reconciler result that sled-agent could + /// have emitted if reconciliation succeeded. + /// + /// This method should only be used by tests and dev tools; real code should + /// look at the actual `last_reconciliation` value from the parent + /// [`Inventory`]. + pub fn debug_assume_success(config: OmicronSledConfig) -> Self { + let mut ret = Self { + // These fields will be filled in by `debug_update_assume_success`. + last_reconciled_config: OmicronSledConfig::default(), + external_disks: BTreeMap::new(), + datasets: BTreeMap::new(), + orphaned_datasets: IdOrdMap::new(), + zones: BTreeMap::new(), + remove_mupdate_override: None, + + // These fields will not. + boot_partitions: BootPartitionContents::debug_assume_success(), + }; + + ret.debug_update_assume_success(config); + + ret + } + + /// Given a sled config, update an existing reconciler result to simulate an + /// output that sled-agent could have emitted if reconciliation succeeded. + /// + /// This method should only be used by tests and dev tools; real code should + /// look at the actual `last_reconciliation` value from the parent + /// [`Inventory`]. + pub fn debug_update_assume_success(&mut self, config: OmicronSledConfig) { + let external_disks = config + .disks + .iter() + .map(|d| (d.id, ConfigReconcilerInventoryResult::Ok)) + .collect(); + let datasets = config + .datasets + .iter() + .map(|d| (d.id, ConfigReconcilerInventoryResult::Ok)) + .collect(); + let zones = config + .zones + .iter() + .map(|z| (z.id, ConfigReconcilerInventoryResult::Ok)) + .collect(); + let remove_mupdate_override = + config.remove_mupdate_override.map(|_| { + RemoveMupdateOverrideInventory { + boot_disk_result: Ok( + RemoveMupdateOverrideBootSuccessInventory::Removed, + ), + non_boot_message: "mupdate override successfully removed \ + on non-boot disks" + .to_owned(), + } + }); + + self.last_reconciled_config = config; + self.external_disks = external_disks; + self.datasets = datasets; + self.orphaned_datasets = IdOrdMap::new(); + self.zones = zones; + self.remove_mupdate_override = remove_mupdate_override; + } +} + +/// Status of the sled-agent-config-reconciler task. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(tag = "status", rename_all = "snake_case")] +pub enum ConfigReconcilerInventoryStatus { + /// The reconciler task has not yet run for the first time since sled-agent + /// started. + NotYetRun, + /// The reconciler task is actively running. + Running { + config: Box, + started_at: DateTime, + running_for: Duration, + }, + /// The reconciler task is currently idle, but previously did complete a + /// reconciliation attempt. + /// + /// This variant does not include the `OmicronSledConfig` used in the last + /// attempt, because that's always available via + /// [`ConfigReconcilerInventory::last_reconciled_config`]. + Idle { completed_at: DateTime, ran_for: Duration }, +} + +/// Describes the set of Reconfigurator-managed configuration elements of a sled +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +pub struct OmicronSledConfig { + pub generation: Generation, + // Serialize and deserialize disks, datasets, and zones as maps for + // backwards compatibility. Newer IdOrdMaps should not use IdOrdMapAsMap. + #[serde( + with = "iddqd::id_ord_map::IdOrdMapAsMap::" + )] + pub disks: IdOrdMap, + #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] + pub datasets: IdOrdMap, + #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] + pub zones: IdOrdMap, + pub remove_mupdate_override: Option, + #[serde(default = "HostPhase2DesiredSlots::current_contents")] + pub host_phase_2: HostPhase2DesiredSlots, +} + +impl Default for OmicronSledConfig { + fn default() -> Self { + Self { + generation: Generation::new(), + disks: IdOrdMap::default(), + datasets: IdOrdMap::default(), + zones: IdOrdMap::default(), + remove_mupdate_override: None, + host_phase_2: HostPhase2DesiredSlots::current_contents(), + } + } +} + +impl Ledgerable for OmicronSledConfig { + fn is_newer_than(&self, other: &Self) -> bool { + self.generation > other.generation + } + + fn generation_bump(&mut self) { + // DO NOTHING! + // + // Generation bumps must only ever come from nexus and will be encoded + // in the struct itself + } +} + +/// Describes the set of Omicron-managed zones running on a sled +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +pub struct OmicronZonesConfig { + /// generation number of this configuration + /// + /// This generation number is owned by the control plane (i.e., RSS or + /// Nexus, depending on whether RSS-to-Nexus handoff has happened). It + /// should not be bumped within Sled Agent. + /// + /// Sled Agent rejects attempts to set the configuration to a generation + /// older than the one it's currently running. + pub generation: Generation, + + /// list of running zones + pub zones: Vec, +} + +impl OmicronZonesConfig { + /// Generation 1 of `OmicronZonesConfig` is always the set of no zones. + pub const INITIAL_GENERATION: Generation = Generation::from_u32(1); +} + +/// Describes one Omicron-managed zone running on a sled +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +pub struct OmicronZoneConfig { + pub id: OmicronZoneUuid, + + /// The pool on which we'll place this zone's root filesystem. + /// + /// Note that the root filesystem is transient -- the sled agent is + /// permitted to destroy this dataset each time the zone is initialized. + pub filesystem_pool: Option, + pub zone_type: OmicronZoneType, + // Use `InstallDataset` if this field is not present in a deserialized + // blueprint or ledger. + #[serde(default = "OmicronZoneImageSource::deserialize_default")] + pub image_source: OmicronZoneImageSource, +} + +impl IdOrdItem for OmicronZoneConfig { + type Key<'a> = OmicronZoneUuid; + + fn key(&self) -> Self::Key<'_> { + self.id + } + + id_upcast!(); +} + +impl OmicronZoneConfig { + /// Returns the underlay IP address associated with this zone. + /// + /// Assumes all zone have exactly one underlay IP address (which is + /// currently true). + pub fn underlay_ip(&self) -> Ipv6Addr { + self.zone_type.underlay_ip() + } + + pub fn zone_name(&self) -> String { + illumos_utils::running_zone::InstalledZone::get_zone_name( + self.zone_type.kind().zone_prefix(), + Some(self.id), + ) + } + + pub fn dataset_name(&self) -> Option { + self.zone_type.dataset_name() + } +} + +/// Describes what kind of zone this is (i.e., what component is running in it) +/// as well as any type-specific configuration +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum OmicronZoneType { + BoundaryNtp { + address: SocketAddrV6, + ntp_servers: Vec, + dns_servers: Vec, + domain: Option, + /// The service vNIC providing outbound connectivity using OPTE. + nic: NetworkInterface, + /// The SNAT configuration for outbound connections. + snat_cfg: SourceNatConfigGeneric, + }, + + /// Type of clickhouse zone used for a single node clickhouse deployment + Clickhouse { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + + /// A zone used to run a Clickhouse Keeper node + /// + /// Keepers are only used in replicated clickhouse setups + ClickhouseKeeper { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + + /// A zone used to run a Clickhouse Server in a replicated deployment + ClickhouseServer { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + + CockroachDb { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + + Crucible { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + CruciblePantry { + address: SocketAddrV6, + }, + ExternalDns { + dataset: OmicronZoneDataset, + /// The address at which the external DNS server API is reachable. + http_address: SocketAddrV6, + /// The address at which the external DNS server is reachable. + dns_address: SocketAddr, + /// The service vNIC providing external connectivity using OPTE. + nic: NetworkInterface, + }, + InternalDns { + dataset: OmicronZoneDataset, + http_address: SocketAddrV6, + dns_address: SocketAddrV6, + /// The addresses in the global zone which should be created + /// + /// For the DNS service, which exists outside the sleds's typical subnet + /// - adding an address in the GZ is necessary to allow inter-zone + /// traffic routing. + gz_address: Ipv6Addr, + + /// The address is also identified with an auxiliary bit of information + /// to ensure that the created global zone address can have a unique + /// name. + gz_address_index: u32, + }, + InternalNtp { + address: SocketAddrV6, + }, + Nexus { + /// The address at which the internal nexus server is reachable. + internal_address: SocketAddrV6, + /// The port at which the internal lockstep server is reachable. This + /// shares the same IP address with `internal_address`. + #[serde(default = "default_nexus_lockstep_port")] + lockstep_port: u16, + /// The address at which the external nexus server is reachable. + external_ip: IpAddr, + /// The service vNIC providing external connectivity using OPTE. + nic: NetworkInterface, + /// Whether Nexus's external endpoint should use TLS + external_tls: bool, + /// External DNS servers Nexus can use to resolve external hosts. + external_dns_servers: Vec, + }, + Oximeter { + address: SocketAddrV6, + }, +} + +impl OmicronZoneType { + /// Returns the [`ZoneKind`] corresponding to this variant. + pub fn kind(&self) -> ZoneKind { + match self { + OmicronZoneType::BoundaryNtp { .. } => ZoneKind::BoundaryNtp, + OmicronZoneType::Clickhouse { .. } => ZoneKind::Clickhouse, + OmicronZoneType::ClickhouseKeeper { .. } => { + ZoneKind::ClickhouseKeeper + } + OmicronZoneType::ClickhouseServer { .. } => { + ZoneKind::ClickhouseServer + } + OmicronZoneType::CockroachDb { .. } => ZoneKind::CockroachDb, + OmicronZoneType::Crucible { .. } => ZoneKind::Crucible, + OmicronZoneType::CruciblePantry { .. } => ZoneKind::CruciblePantry, + OmicronZoneType::ExternalDns { .. } => ZoneKind::ExternalDns, + OmicronZoneType::InternalDns { .. } => ZoneKind::InternalDns, + OmicronZoneType::InternalNtp { .. } => ZoneKind::InternalNtp, + OmicronZoneType::Nexus { .. } => ZoneKind::Nexus, + OmicronZoneType::Oximeter { .. } => ZoneKind::Oximeter, + } + } + + /// Does this zone require time synchronization before it is initialized?" + /// + /// This function is somewhat conservative - the set of services + /// that can be launched before timesync has completed is intentionally kept + /// small, since it would be easy to add a service that expects time to be + /// reasonably synchronized. + pub fn requires_timesync(&self) -> bool { + match self { + // These zones can be initialized and started before time has been + // synchronized. For the NTP zones, this should be self-evident -- + // we need the NTP zone to actually perform time synchronization! + // + // The DNS zone is a bit of an exception here, since the NTP zone + // itself may rely on DNS lookups as a dependency. + OmicronZoneType::BoundaryNtp { .. } + | OmicronZoneType::InternalNtp { .. } + | OmicronZoneType::InternalDns { .. } => false, + _ => true, + } + } + + /// Returns the underlay IP address associated with this zone. + /// + /// Assumes all zone have exactly one underlay IP address (which is + /// currently true). + pub fn underlay_ip(&self) -> Ipv6Addr { + match self { + OmicronZoneType::BoundaryNtp { address, .. } + | OmicronZoneType::Clickhouse { address, .. } + | OmicronZoneType::ClickhouseKeeper { address, .. } + | OmicronZoneType::ClickhouseServer { address, .. } + | OmicronZoneType::CockroachDb { address, .. } + | OmicronZoneType::Crucible { address, .. } + | OmicronZoneType::CruciblePantry { address } + | OmicronZoneType::ExternalDns { http_address: address, .. } + | OmicronZoneType::InternalNtp { address } + | OmicronZoneType::Nexus { internal_address: address, .. } + | OmicronZoneType::Oximeter { address } => *address.ip(), + OmicronZoneType::InternalDns { + http_address: address, + dns_address, + .. + } => { + // InternalDns is the only variant that carries two + // `SocketAddrV6`s that are both on the underlay network. We + // expect these to have the same IP address. + debug_assert_eq!(address.ip(), dns_address.ip()); + *address.ip() + } + } + } + + /// Identifies whether this is an NTP zone + pub fn is_ntp(&self) -> bool { + match self { + OmicronZoneType::BoundaryNtp { .. } + | OmicronZoneType::InternalNtp { .. } => true, + + OmicronZoneType::Clickhouse { .. } + | OmicronZoneType::ClickhouseKeeper { .. } + | OmicronZoneType::ClickhouseServer { .. } + | OmicronZoneType::CockroachDb { .. } + | OmicronZoneType::Crucible { .. } + | OmicronZoneType::CruciblePantry { .. } + | OmicronZoneType::ExternalDns { .. } + | OmicronZoneType::InternalDns { .. } + | OmicronZoneType::Nexus { .. } + | OmicronZoneType::Oximeter { .. } => false, + } + } + + /// Identifies whether this is a boundary NTP zone + pub fn is_boundary_ntp(&self) -> bool { + matches!(self, OmicronZoneType::BoundaryNtp { .. }) + } + + /// Identifies whether this is a Nexus zone + pub fn is_nexus(&self) -> bool { + match self { + OmicronZoneType::Nexus { .. } => true, + + OmicronZoneType::BoundaryNtp { .. } + | OmicronZoneType::InternalNtp { .. } + | OmicronZoneType::Clickhouse { .. } + | OmicronZoneType::ClickhouseKeeper { .. } + | OmicronZoneType::ClickhouseServer { .. } + | OmicronZoneType::CockroachDb { .. } + | OmicronZoneType::Crucible { .. } + | OmicronZoneType::CruciblePantry { .. } + | OmicronZoneType::ExternalDns { .. } + | OmicronZoneType::InternalDns { .. } + | OmicronZoneType::Oximeter { .. } => false, + } + } + + /// Identifies whether this a Crucible (not Crucible pantry) zone + pub fn is_crucible(&self) -> bool { + match self { + OmicronZoneType::Crucible { .. } => true, + + OmicronZoneType::BoundaryNtp { .. } + | OmicronZoneType::InternalNtp { .. } + | OmicronZoneType::Clickhouse { .. } + | OmicronZoneType::ClickhouseKeeper { .. } + | OmicronZoneType::ClickhouseServer { .. } + | OmicronZoneType::CockroachDb { .. } + | OmicronZoneType::CruciblePantry { .. } + | OmicronZoneType::ExternalDns { .. } + | OmicronZoneType::InternalDns { .. } + | OmicronZoneType::Nexus { .. } + | OmicronZoneType::Oximeter { .. } => false, + } + } + + /// This zone's external IP + pub fn external_ip(&self) -> Option { + match self { + OmicronZoneType::Nexus { external_ip, .. } => Some(*external_ip), + OmicronZoneType::ExternalDns { dns_address, .. } => { + Some(dns_address.ip()) + } + OmicronZoneType::BoundaryNtp { snat_cfg, .. } => Some(snat_cfg.ip), + + OmicronZoneType::InternalNtp { .. } + | OmicronZoneType::Clickhouse { .. } + | OmicronZoneType::ClickhouseKeeper { .. } + | OmicronZoneType::ClickhouseServer { .. } + | OmicronZoneType::CockroachDb { .. } + | OmicronZoneType::Crucible { .. } + | OmicronZoneType::CruciblePantry { .. } + | OmicronZoneType::InternalDns { .. } + | OmicronZoneType::Oximeter { .. } => None, + } + } + + /// The service vNIC providing external connectivity to this zone + pub fn service_vnic(&self) -> Option<&NetworkInterface> { + match self { + OmicronZoneType::Nexus { nic, .. } + | OmicronZoneType::ExternalDns { nic, .. } + | OmicronZoneType::BoundaryNtp { nic, .. } => Some(nic), + + OmicronZoneType::InternalNtp { .. } + | OmicronZoneType::Clickhouse { .. } + | OmicronZoneType::ClickhouseKeeper { .. } + | OmicronZoneType::ClickhouseServer { .. } + | OmicronZoneType::CockroachDb { .. } + | OmicronZoneType::Crucible { .. } + | OmicronZoneType::CruciblePantry { .. } + | OmicronZoneType::InternalDns { .. } + | OmicronZoneType::Oximeter { .. } => None, + } + } + + /// If this kind of zone has an associated dataset, return the dataset's + /// name. Otherwise, return `None`. + pub fn dataset_name(&self) -> Option { + let (dataset, dataset_kind) = match self { + OmicronZoneType::BoundaryNtp { .. } + | OmicronZoneType::InternalNtp { .. } + | OmicronZoneType::Nexus { .. } + | OmicronZoneType::Oximeter { .. } + | OmicronZoneType::CruciblePantry { .. } => None, + OmicronZoneType::Clickhouse { dataset, .. } => { + Some((dataset, DatasetKind::Clickhouse)) + } + OmicronZoneType::ClickhouseKeeper { dataset, .. } => { + Some((dataset, DatasetKind::ClickhouseKeeper)) + } + OmicronZoneType::ClickhouseServer { dataset, .. } => { + Some((dataset, DatasetKind::ClickhouseServer)) + } + OmicronZoneType::CockroachDb { dataset, .. } => { + Some((dataset, DatasetKind::Cockroach)) + } + OmicronZoneType::Crucible { dataset, .. } => { + Some((dataset, DatasetKind::Crucible)) + } + OmicronZoneType::ExternalDns { dataset, .. } => { + Some((dataset, DatasetKind::ExternalDns)) + } + OmicronZoneType::InternalDns { dataset, .. } => { + Some((dataset, DatasetKind::InternalDns)) + } + }?; + + Some(DatasetName::new(dataset.pool_name, dataset_kind)) + } +} + +fn default_nexus_lockstep_port() -> u16 { + omicron_common::address::NEXUS_LOCKSTEP_PORT +} + +use omicron_common::api::external; + +impl TryFrom for Inventory { + type Error = external::Error; + + fn try_from(v10: v10::inventory::Inventory) -> Result { + Ok(Self { + sled_id: v10.sled_id, + sled_agent_address: v10.sled_agent_address, + sled_role: v10.sled_role, + baseboard: v10.baseboard, + usable_hardware_threads: v10.usable_hardware_threads, + usable_physical_ram: v10.usable_physical_ram, + cpu_family: v10.cpu_family, + reservoir_size: v10.reservoir_size, + disks: v10.disks, + zpools: v10.zpools, + datasets: v10.datasets, + ledgered_sled_config: v10 + .ledgered_sled_config + .map(TryInto::try_into) + .transpose()?, + reconciler_status: v10.reconciler_status.try_into()?, + last_reconciliation: v10 + .last_reconciliation + .map(TryInto::try_into) + .transpose()?, + zone_image_resolver: v10.zone_image_resolver, + }) + } +} + +impl TryFrom for OmicronSledConfig { + type Error = external::Error; + + fn try_from( + v10: v10::inventory::OmicronSledConfig, + ) -> Result { + Ok(Self { + generation: v10.generation, + disks: v10.disks, + datasets: v10.datasets, + zones: v10 + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + remove_mupdate_override: v10.remove_mupdate_override, + host_phase_2: v10.host_phase_2, + }) + } +} + +impl TryFrom for OmicronZoneConfig { + type Error = external::Error; + + fn try_from( + v10: v10::inventory::OmicronZoneConfig, + ) -> Result { + Ok(Self { + id: v10.id, + filesystem_pool: v10.filesystem_pool, + zone_type: v10.zone_type.try_into()?, + image_source: v10.image_source, + }) + } +} + +impl TryFrom for OmicronZoneType { + type Error = external::Error; + + fn try_from( + v10: v10::inventory::OmicronZoneType, + ) -> Result { + match v10 { + v10::inventory::OmicronZoneType::BoundaryNtp { + address, + ntp_servers, + dns_servers, + domain, + nic, + snat_cfg, + } => { + let (first_port, last_port) = snat_cfg.port_range_raw(); + let snat_cfg = SourceNatConfigGeneric::new( + snat_cfg.ip, + first_port, + last_port, + ) + .map_err(|e| external::Error::invalid_request(e.to_string()))?; + Ok(Self::BoundaryNtp { + address, + ntp_servers, + dns_servers, + domain, + nic, + snat_cfg, + }) + } + v10::inventory::OmicronZoneType::Clickhouse { + address, + dataset, + } => Ok(OmicronZoneType::Clickhouse { address, dataset }), + v10::inventory::OmicronZoneType::ClickhouseKeeper { + address, + dataset, + } => Ok(OmicronZoneType::ClickhouseKeeper { address, dataset }), + v10::inventory::OmicronZoneType::ClickhouseServer { + address, + dataset, + } => Ok(OmicronZoneType::ClickhouseServer { address, dataset }), + v10::inventory::OmicronZoneType::CockroachDb { + address, + dataset, + } => Ok(OmicronZoneType::CockroachDb { address, dataset }), + v10::inventory::OmicronZoneType::Crucible { address, dataset } => { + Ok(OmicronZoneType::Crucible { address, dataset }) + } + v10::inventory::OmicronZoneType::CruciblePantry { address } => { + Ok(OmicronZoneType::CruciblePantry { address }) + } + v10::inventory::OmicronZoneType::ExternalDns { + dataset, + http_address, + dns_address, + nic, + } => Ok(OmicronZoneType::ExternalDns { + dataset, + http_address, + dns_address, + nic, + }), + v10::inventory::OmicronZoneType::InternalDns { + dataset, + http_address, + dns_address, + gz_address, + gz_address_index, + } => Ok(OmicronZoneType::InternalDns { + dataset, + http_address, + dns_address, + gz_address, + gz_address_index, + }), + v10::inventory::OmicronZoneType::InternalNtp { address } => { + Ok(OmicronZoneType::InternalNtp { address }) + } + v10::inventory::OmicronZoneType::Nexus { + internal_address, + lockstep_port, + external_ip, + nic, + external_tls, + external_dns_servers, + } => Ok(OmicronZoneType::Nexus { + internal_address, + lockstep_port, + external_ip, + nic, + external_tls, + external_dns_servers, + }), + v10::inventory::OmicronZoneType::Oximeter { address } => { + Ok(OmicronZoneType::Oximeter { address }) + } + } + } +} + +impl TryFrom + for ConfigReconcilerInventory +{ + type Error = external::Error; + + fn try_from( + v10: v10::inventory::ConfigReconcilerInventory, + ) -> Result { + Ok(Self { + last_reconciled_config: v10.last_reconciled_config.try_into()?, + external_disks: v10.external_disks, + datasets: v10.datasets, + orphaned_datasets: v10.orphaned_datasets, + zones: v10.zones, + boot_partitions: v10.boot_partitions, + remove_mupdate_override: v10.remove_mupdate_override, + }) + } +} + +impl TryFrom + for ConfigReconcilerInventoryStatus +{ + type Error = external::Error; + + fn try_from( + v10: v10::inventory::ConfigReconcilerInventoryStatus, + ) -> Result { + match v10 { + v10::inventory::ConfigReconcilerInventoryStatus::NotYetRun => { + Ok(ConfigReconcilerInventoryStatus::NotYetRun) + } + v10::inventory::ConfigReconcilerInventoryStatus::Running { + config, + started_at, + running_for, + } => Ok(ConfigReconcilerInventoryStatus::Running { + config: Box::new((*config).try_into()?), + started_at, + running_for, + }), + v10::inventory::ConfigReconcilerInventoryStatus::Idle { + completed_at, + ran_for, + } => Ok(ConfigReconcilerInventoryStatus::Idle { + completed_at, + ran_for, + }), + } + } +} + +impl TryFrom for OmicronZonesConfig { + type Error = external::Error; + + fn try_from( + v10: v10::inventory::OmicronZonesConfig, + ) -> Result { + Ok(Self { + generation: v10.generation, + zones: v10 + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} + +// Conversions from v11 to v10 for response types +impl TryFrom for v10::inventory::Inventory { + type Error = external::Error; + + fn try_from(v11: Inventory) -> Result { + Ok(Self { + sled_id: v11.sled_id, + sled_agent_address: v11.sled_agent_address, + sled_role: v11.sled_role, + baseboard: v11.baseboard, + usable_hardware_threads: v11.usable_hardware_threads, + usable_physical_ram: v11.usable_physical_ram, + cpu_family: v11.cpu_family, + reservoir_size: v11.reservoir_size, + disks: v11.disks, + zpools: v11.zpools, + datasets: v11.datasets, + ledgered_sled_config: v11 + .ledgered_sled_config + .map(TryInto::try_into) + .transpose()?, + reconciler_status: v11.reconciler_status.try_into()?, + last_reconciliation: v11 + .last_reconciliation + .map(TryInto::try_into) + .transpose()?, + zone_image_resolver: v11.zone_image_resolver, + }) + } +} + +impl TryFrom for v10::inventory::OmicronSledConfig { + type Error = external::Error; + + fn try_from(v11: OmicronSledConfig) -> Result { + Ok(Self { + generation: v11.generation, + disks: v11.disks, + datasets: v11.datasets, + zones: v11 + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + remove_mupdate_override: v11.remove_mupdate_override, + host_phase_2: v11.host_phase_2, + }) + } +} + +impl TryFrom for v10::inventory::OmicronZoneConfig { + type Error = external::Error; + + fn try_from(v11: OmicronZoneConfig) -> Result { + Ok(Self { + id: v11.id, + filesystem_pool: v11.filesystem_pool, + zone_type: v11.zone_type.try_into()?, + image_source: v11.image_source, + }) + } +} + +impl TryFrom for v10::inventory::OmicronZoneType { + type Error = external::Error; + + fn try_from(v11: OmicronZoneType) -> Result { + use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; + + match v11 { + OmicronZoneType::BoundaryNtp { + address, + ntp_servers, + dns_servers, + domain, + nic, + snat_cfg, + } => { + let (first_port, last_port) = snat_cfg.port_range_raw(); + Ok(v10::inventory::OmicronZoneType::BoundaryNtp { + address, + ntp_servers, + dns_servers, + domain, + nic, + snat_cfg: SourceNatConfig::new( + snat_cfg.ip, + first_port, + last_port, + ) + .map_err(|e| { + external::Error::invalid_request(format!( + "invalid SNAT config: {e}" + )) + })?, + }) + } + OmicronZoneType::Clickhouse { address, dataset } => { + Ok(v10::inventory::OmicronZoneType::Clickhouse { + address, + dataset, + }) + } + OmicronZoneType::ClickhouseKeeper { address, dataset } => { + Ok(v10::inventory::OmicronZoneType::ClickhouseKeeper { + address, + dataset, + }) + } + OmicronZoneType::ClickhouseServer { address, dataset } => { + Ok(v10::inventory::OmicronZoneType::ClickhouseServer { + address, + dataset, + }) + } + OmicronZoneType::CockroachDb { address, dataset } => { + Ok(v10::inventory::OmicronZoneType::CockroachDb { + address, + dataset, + }) + } + OmicronZoneType::Crucible { address, dataset } => { + Ok(v10::inventory::OmicronZoneType::Crucible { + address, + dataset, + }) + } + OmicronZoneType::CruciblePantry { address } => { + Ok(v10::inventory::OmicronZoneType::CruciblePantry { address }) + } + OmicronZoneType::ExternalDns { + dataset, + http_address, + dns_address, + nic, + } => Ok(v10::inventory::OmicronZoneType::ExternalDns { + dataset, + http_address, + dns_address, + nic, + }), + OmicronZoneType::InternalDns { + dataset, + http_address, + dns_address, + gz_address, + gz_address_index, + } => Ok(v10::inventory::OmicronZoneType::InternalDns { + dataset, + http_address, + dns_address, + gz_address, + gz_address_index, + }), + OmicronZoneType::InternalNtp { address } => { + Ok(v10::inventory::OmicronZoneType::InternalNtp { address }) + } + OmicronZoneType::Nexus { + internal_address, + lockstep_port, + external_ip, + nic, + external_tls, + external_dns_servers, + } => Ok(v10::inventory::OmicronZoneType::Nexus { + internal_address, + lockstep_port, + external_ip, + nic, + external_tls, + external_dns_servers, + }), + OmicronZoneType::Oximeter { address } => { + Ok(v10::inventory::OmicronZoneType::Oximeter { address }) + } + } + } +} + +impl TryFrom + for v10::inventory::ConfigReconcilerInventory +{ + type Error = external::Error; + + fn try_from(v11: ConfigReconcilerInventory) -> Result { + Ok(Self { + last_reconciled_config: v11.last_reconciled_config.try_into()?, + external_disks: v11.external_disks, + datasets: v11.datasets, + orphaned_datasets: v11.orphaned_datasets, + zones: v11.zones, + boot_partitions: v11.boot_partitions, + remove_mupdate_override: v11.remove_mupdate_override, + }) + } +} + +impl TryFrom + for v10::inventory::ConfigReconcilerInventoryStatus +{ + type Error = external::Error; + + fn try_from( + v11: ConfigReconcilerInventoryStatus, + ) -> Result { + match v11 { + ConfigReconcilerInventoryStatus::NotYetRun => { + Ok(v10::inventory::ConfigReconcilerInventoryStatus::NotYetRun) + } + ConfigReconcilerInventoryStatus::Running { + config, + started_at, + running_for, + } => Ok(v10::inventory::ConfigReconcilerInventoryStatus::Running { + config: Box::new((*config).try_into()?), + started_at, + running_for, + }), + ConfigReconcilerInventoryStatus::Idle { completed_at, ran_for } => { + Ok(v10::inventory::ConfigReconcilerInventoryStatus::Idle { + completed_at, + ran_for, + }) + } + } + } +} + +impl TryFrom for v10::inventory::OmicronZonesConfig { + type Error = external::Error; + + fn try_from(v11: OmicronZonesConfig) -> Result { + Ok(Self { + generation: v11.generation, + zones: v11 + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} diff --git a/sled-agent/types/versions/src/add_dual_stack_external_ip_config/mod.rs b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/mod.rs new file mode 100644 index 00000000000..755bff43ea2 --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_external_ip_config/mod.rs @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `ADD_DUAL_STACK_EXTERNAL_IP_CONFIG` of the Sled Agent API. +//! +//! This version changes `SourceNatConfig` to `SourceNatConfigGeneric` to +//! support dual-stack external IP configuration. + +pub mod firewall_rules; +pub mod instance; +pub mod inventory; diff --git a/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/firewall_rules.rs b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/firewall_rules.rs new file mode 100644 index 00000000000..ce3fb7bdf61 --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/firewall_rules.rs @@ -0,0 +1,37 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Firewall rule types for version `ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES`. + +use crate::v9; +use omicron_common::api::external; +use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Update firewall rules for a VPC +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct VpcFirewallRulesEnsureBody { + pub vni: external::Vni, + pub rules: Vec, +} + +impl TryFrom + for VpcFirewallRulesEnsureBody +{ + type Error = external::Error; + + fn try_from( + v9: v9::firewall_rules::VpcFirewallRulesEnsureBody, + ) -> Result { + Ok(Self { + vni: v9.vni, + rules: v9 + .rules + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + }) + } +} diff --git a/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/instance.rs b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/instance.rs new file mode 100644 index 00000000000..a54cc865c04 --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/instance.rs @@ -0,0 +1,144 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::net::{IpAddr, SocketAddr}; + +use omicron_common::api::external; +use omicron_common::api::external::Hostname; +use omicron_common::api::internal::nexus::VmmRuntimeState; +use omicron_common::api::internal::shared::NetworkInterface; +use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; +use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; +use omicron_common::api::internal::shared::{DelegatedZvol, DhcpConfig}; +use omicron_uuid_kinds::InstanceUuid; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::v1::instance::InstanceMetadata; +use crate::v1::instance::VmmSpec; +use crate::v7::instance::InstanceMulticastMembership; +use crate::v9; + +/// The body of a request to ensure that a instance and VMM are known to a sled +/// agent. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct InstanceEnsureBody { + /// The virtual hardware configuration this virtual machine should have when + /// it is started. + pub vmm_spec: VmmSpec, + + /// Information about the sled-local configuration that needs to be + /// established to make the VM's virtual hardware fully functional. + pub local_config: InstanceSledLocalConfig, + + /// The initial VMM runtime state for the VMM being registered. + pub vmm_runtime: VmmRuntimeState, + + /// The ID of the instance for which this VMM is being created. + pub instance_id: InstanceUuid, + + /// The ID of the migration in to this VMM, if this VMM is being + /// ensured is part of a migration in. If this is `None`, the VMM is not + /// being created due to a migration. + pub migration_id: Option, + + /// The address at which this VMM should serve a Propolis server API. + pub propolis_addr: SocketAddr, + + /// Metadata used to track instance statistics. + pub metadata: InstanceMetadata, +} + +/// Describes sled-local configuration that a sled-agent must establish to make +/// the instance's virtual hardware fully functional. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct InstanceSledLocalConfig { + pub hostname: Hostname, + pub nics: Vec, + pub source_nat: SourceNatConfig, + /// Zero or more external IP addresses (either floating or ephemeral), + /// provided to an instance to allow inbound connectivity. + pub ephemeral_ip: Option, + pub floating_ips: Vec, + pub multicast_groups: Vec, + pub firewall_rules: Vec, + pub dhcp_config: DhcpConfig, + pub delegated_zvols: Vec, +} + +impl TryFrom for InstanceEnsureBody { + type Error = external::Error; + + fn try_from( + v9: v9::instance::InstanceEnsureBody, + ) -> Result { + Ok(Self { + vmm_spec: v9.vmm_spec, + local_config: v9.local_config.try_into()?, + vmm_runtime: v9.vmm_runtime, + instance_id: v9.instance_id, + migration_id: v9.migration_id, + propolis_addr: v9.propolis_addr, + metadata: v9.metadata, + }) + } +} + +impl TryFrom + for InstanceSledLocalConfig +{ + type Error = external::Error; + + fn try_from( + v9: v9::instance::InstanceSledLocalConfig, + ) -> Result { + let firewall_rules = v9 + .firewall_rules + .into_iter() + .map(TryInto::try_into) + .collect::, _>>()?; + + Ok(Self { + hostname: v9.hostname, + nics: v9 + .nics + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + source_nat: v9.source_nat, + ephemeral_ip: v9.ephemeral_ip, + floating_ips: v9.floating_ips, + multicast_groups: v9.multicast_groups, + firewall_rules, + dhcp_config: v9.dhcp_config, + delegated_zvols: v9.delegated_zvols, + }) + } +} + +impl TryFrom + for ResolvedVpcFirewallRule +{ + type Error = external::Error; + + fn try_from( + v1: crate::v1::instance::ResolvedVpcFirewallRule, + ) -> Result { + Ok(Self { + status: v1.status, + direction: v1.direction, + targets: v1 + .targets + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + filter_hosts: v1.filter_hosts, + filter_ports: v1.filter_ports, + filter_protocols: v1.filter_protocols, + action: v1.action, + priority: v1.priority, + }) + } +} diff --git a/sled-agent/types/src/inventory/v9.rs b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/inventory.rs similarity index 56% rename from sled-agent/types/src/inventory/v9.rs rename to sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/inventory.rs index 6c5520f227c..e986bcb130e 100644 --- a/sled-agent/types/src/inventory/v9.rs +++ b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/inventory.rs @@ -2,64 +2,42 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Sled-agent API types that changed from v9 to v10. - -use crate::instance::InstanceMetadata; -use crate::instance::InstanceMulticastMembership; -use crate::instance::VmmSpec; -use crate::inventory::v10; -use chrono::DateTime; -use chrono::Utc; +use std::collections::BTreeMap; +use std::net::{IpAddr, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::time::Duration; + +use chrono::{DateTime, Utc}; use iddqd::IdOrdItem; use iddqd::IdOrdMap; use iddqd::id_upcast; -use nexus_sled_agent_shared::inventory::BootPartitionContents; -use nexus_sled_agent_shared::inventory::ConfigReconcilerInventoryResult; -use nexus_sled_agent_shared::inventory::HostPhase2DesiredSlots; -use nexus_sled_agent_shared::inventory::InventoryDataset; -use nexus_sled_agent_shared::inventory::InventoryDisk; -use nexus_sled_agent_shared::inventory::InventoryZpool; -use nexus_sled_agent_shared::inventory::OmicronZoneDataset; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; -use nexus_sled_agent_shared::inventory::OrphanedDataset; -use nexus_sled_agent_shared::inventory::RemoveMupdateOverrideInventory; -use nexus_sled_agent_shared::inventory::SledRole; -use nexus_sled_agent_shared::inventory::ZoneImageResolverInventory; -use omicron_common::api::external; -use omicron_common::api::external::ByteCount; -use omicron_common::api::external::Generation; -use omicron_common::api::external::Hostname; -use omicron_common::api::internal::nexus::HostIdentifier; -use omicron_common::api::internal::nexus::VmmRuntimeState; -use omicron_common::api::internal::shared::DelegatedZvol; -use omicron_common::api::internal::shared::DhcpConfig; -use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; -use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; -use omicron_common::disk::DatasetConfig; -use omicron_common::disk::OmicronPhysicalDiskConfig; -use omicron_common::zpool_name::ZpoolName; -use omicron_uuid_kinds::DatasetUuid; -use omicron_uuid_kinds::InstanceUuid; -use omicron_uuid_kinds::MupdateOverrideUuid; -use omicron_uuid_kinds::OmicronZoneUuid; -use omicron_uuid_kinds::PhysicalDiskUuid; +use omicron_common::ledger::Ledgerable; +use omicron_common::{ + api::{ + external::{self, ByteCount, Generation}, + internal::shared::{ + NetworkInterface, external_ip::v1::SourceNatConfig, + }, + }, + disk::{DatasetConfig, OmicronPhysicalDiskConfig}, + zpool_name::ZpoolName, +}; use omicron_uuid_kinds::SledUuid; +use omicron_uuid_kinds::{DatasetUuid, OmicronZoneUuid}; +use omicron_uuid_kinds::{MupdateOverrideUuid, PhysicalDiskUuid}; use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use sled_hardware_types::Baseboard; -use sled_hardware_types::SledCpuFamily; -use std::collections::BTreeMap; -use std::collections::HashSet; -use std::net::IpAddr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV6; -use std::time::Duration; -use uuid::Uuid; +use serde::{Deserialize, Serialize}; +use sled_hardware_types::{Baseboard, SledCpuFamily}; + +use crate::v1::inventory::{ + BootPartitionContents, ConfigReconcilerInventoryResult, + HostPhase2DesiredSlots, InventoryDataset, InventoryDisk, InventoryZpool, + OmicronZoneDataset, OmicronZoneImageSource, OrphanedDataset, + RemoveMupdateOverrideInventory, SledRole, ZoneImageResolverInventory, +}; +use crate::v4; /// Identity and basic status information about this sled agent -#[derive(Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] pub struct Inventory { pub sled_id: SledUuid, pub sled_agent_address: SocketAddrV6, @@ -78,39 +56,52 @@ pub struct Inventory { pub zone_image_resolver: ZoneImageResolverInventory, } -impl TryFrom for Inventory { - type Error = external::Error; +/// Describes the last attempt made by the sled-agent-config-reconciler to +/// reconcile the current sled config against the actual state of the sled. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct ConfigReconcilerInventory { + pub last_reconciled_config: OmicronSledConfig, + pub external_disks: + BTreeMap, + pub datasets: BTreeMap, + pub orphaned_datasets: IdOrdMap, + pub zones: BTreeMap, + pub boot_partitions: BootPartitionContents, + /// The result of removing the mupdate override file on disk. + /// + /// `None` if `remove_mupdate_override` was not provided in the sled config. + pub remove_mupdate_override: Option, +} - fn try_from(value: v10::Inventory) -> Result { - let ledgered_sled_config = - value.ledgered_sled_config.map(TryInto::try_into).transpose()?; - let reconciler_status = value.reconciler_status.try_into()?; - let last_reconciliation = - value.last_reconciliation.map(TryInto::try_into).transpose()?; - Ok(Self { - sled_id: value.sled_id, - sled_agent_address: value.sled_agent_address, - sled_role: value.sled_role, - baseboard: value.baseboard, - usable_hardware_threads: value.usable_hardware_threads, - usable_physical_ram: value.usable_physical_ram, - cpu_family: value.cpu_family, - reservoir_size: value.reservoir_size, - disks: value.disks, - zpools: value.zpools, - datasets: value.datasets, - ledgered_sled_config, - reconciler_status, - last_reconciliation, - zone_image_resolver: value.zone_image_resolver, - }) - } +/// Status of the sled-agent-config-reconciler task. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(tag = "status", rename_all = "snake_case")] +pub enum ConfigReconcilerInventoryStatus { + /// The reconciler task has not yet run for the first time since sled-agent + /// started. + NotYetRun, + /// The reconciler task is actively running. + Running { + config: Box, + started_at: DateTime, + running_for: Duration, + }, + /// The reconciler task is currently idle, but previously did complete a + /// reconciliation attempt. + /// + /// This variant does not include the `OmicronSledConfig` used in the last + /// attempt, because that's always available via + /// [`ConfigReconcilerInventory::last_reconciled_config`]. + Idle { completed_at: DateTime, ran_for: Duration }, } /// Describes the set of Reconfigurator-managed configuration elements of a sled -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub struct OmicronSledConfig { pub generation: Generation, + // Serialize and deserialize disks, datasets, and zones as maps for + // backwards compatibility. Newer IdOrdMaps should not use IdOrdMapAsMap. #[serde( with = "iddqd::id_ord_map::IdOrdMapAsMap::" )] @@ -124,47 +115,60 @@ pub struct OmicronSledConfig { pub host_phase_2: HostPhase2DesiredSlots, } -impl TryFrom for v10::OmicronSledConfig { - type Error = external::Error; - - fn try_from(value: OmicronSledConfig) -> Result { - let zones = value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { - generation: value.generation, - disks: value.disks, - datasets: value.datasets, - zones, - remove_mupdate_override: value.remove_mupdate_override, - host_phase_2: value.host_phase_2, - }) +impl Default for OmicronSledConfig { + fn default() -> Self { + Self { + generation: Generation::new(), + disks: IdOrdMap::default(), + datasets: IdOrdMap::default(), + zones: IdOrdMap::default(), + remove_mupdate_override: None, + host_phase_2: HostPhase2DesiredSlots::current_contents(), + } } } -impl TryFrom for OmicronSledConfig { - type Error = external::Error; - fn try_from(value: v10::OmicronSledConfig) -> Result { - let zones = value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { - generation: value.generation, - disks: value.disks, - datasets: value.datasets, - zones, - remove_mupdate_override: value.remove_mupdate_override, - host_phase_2: value.host_phase_2, - }) +impl Ledgerable for OmicronSledConfig { + fn is_newer_than(&self, other: &Self) -> bool { + self.generation > other.generation + } + + fn generation_bump(&mut self) { + // DO NOTHING! + // + // Generation bumps must only ever come from nexus and will be encoded + // in the struct itself } } +/// Describes the set of Omicron-managed zones running on a sled +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +pub struct OmicronZonesConfig { + /// generation number of this configuration + /// + /// This generation number is owned by the control plane (i.e., RSS or + /// Nexus, depending on whether RSS-to-Nexus handoff has happened). It + /// should not be bumped within Sled Agent. + /// + /// Sled Agent rejects attempts to set the configuration to a generation + /// older than the one it's currently running. + pub generation: Generation, + + /// list of running zones + pub zones: Vec, +} + +impl OmicronZonesConfig { + /// Generation 1 of `OmicronZonesConfig` is always the set of no zones. + pub const INITIAL_GENERATION: Generation = Generation::from_u32(1); +} + /// Describes one Omicron-managed zone running on a sled -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] pub struct OmicronZoneConfig { pub id: OmicronZoneUuid, @@ -190,68 +194,11 @@ impl IdOrdItem for OmicronZoneConfig { id_upcast!(); } -impl TryFrom for v10::OmicronZoneConfig { - type Error = external::Error; - - fn try_from(value: OmicronZoneConfig) -> Result { - Ok(Self { - id: value.id, - filesystem_pool: value.filesystem_pool, - zone_type: value.zone_type.try_into()?, - image_source: value.image_source, - }) - } -} - -impl TryFrom for OmicronZoneConfig { - type Error = external::Error; - - fn try_from(value: v10::OmicronZoneConfig) -> Result { - Ok(Self { - id: value.id, - filesystem_pool: value.filesystem_pool, - zone_type: value.zone_type.try_into()?, - image_source: value.image_source, - }) - } -} - -/// Describes the set of Omicron-managed zones running on a sled -#[derive(Deserialize, Serialize, JsonSchema)] -pub struct OmicronZonesConfig { - /// Generation number of this configuration - /// - /// This generation number is owned by the control plane (i.e., rss or - /// nexus, depending on whether rss-to-nexus handoff has happened). it - /// should not be bumped within sled agent. - /// - /// Sled agent rejects attempts to set the configuration to a generation - /// older than the one it's currently running. - pub generation: Generation, - - /// List of running zones - pub zones: Vec, -} - -impl TryFrom for v10::OmicronZonesConfig { - type Error = external::Error; - - fn try_from(value: OmicronZonesConfig) -> Result { - value - .zones - .into_iter() - .map(TryInto::try_into) - .collect::>() - .map(|zones| v10::OmicronZonesConfig { - generation: value.generation, - zones, - }) - } -} - /// Describes what kind of zone this is (i.e., what component is running in it) /// as well as any type-specific configuration -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] #[serde(tag = "type", rename_all = "snake_case")] pub enum OmicronZoneType { BoundaryNtp { @@ -346,11 +293,73 @@ pub enum OmicronZoneType { }, } -const fn default_nexus_lockstep_port() -> u16 { +fn default_nexus_lockstep_port() -> u16 { omicron_common::address::NEXUS_LOCKSTEP_PORT } -impl TryFrom for v10::OmicronZoneType { +impl TryFrom for v4::inventory::Inventory { + type Error = external::Error; + + fn try_from(value: Inventory) -> Result { + let ledgered_sled_config = + value.ledgered_sled_config.map(TryInto::try_into).transpose()?; + let reconciler_status = value.reconciler_status.try_into()?; + let last_reconciliation = + value.last_reconciliation.map(TryInto::try_into).transpose()?; + Ok(Self { + sled_id: value.sled_id, + sled_agent_address: value.sled_agent_address, + sled_role: value.sled_role, + baseboard: value.baseboard, + usable_hardware_threads: value.usable_hardware_threads, + usable_physical_ram: value.usable_physical_ram, + cpu_family: value.cpu_family, + reservoir_size: value.reservoir_size, + disks: value.disks, + zpools: value.zpools, + datasets: value.datasets, + ledgered_sled_config, + reconciler_status, + last_reconciliation, + zone_image_resolver: value.zone_image_resolver, + }) + } +} + +impl TryFrom for v4::inventory::OmicronSledConfig { + type Error = external::Error; + + fn try_from(value: OmicronSledConfig) -> Result { + let zones = value + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?; + Ok(Self { + generation: value.generation, + disks: value.disks, + datasets: value.datasets, + zones, + remove_mupdate_override: value.remove_mupdate_override, + host_phase_2: value.host_phase_2, + }) + } +} + +impl TryFrom for v4::inventory::OmicronZoneConfig { + type Error = external::Error; + + fn try_from(value: OmicronZoneConfig) -> Result { + Ok(Self { + id: value.id, + filesystem_pool: value.filesystem_pool, + zone_type: value.zone_type.try_into()?, + image_source: value.image_source, + }) + } +} + +impl TryFrom for v4::inventory::OmicronZoneType { type Error = external::Error; fn try_from(value: OmicronZoneType) -> Result { @@ -362,7 +371,7 @@ impl TryFrom for v10::OmicronZoneType { domain, nic, snat_cfg, - } => Ok(Self::BoundaryNtp { + } => Ok(v4::inventory::OmicronZoneType::BoundaryNtp { address, ntp_servers, dns_servers, @@ -371,29 +380,44 @@ impl TryFrom for v10::OmicronZoneType { snat_cfg, }), OmicronZoneType::Clickhouse { address, dataset } => { - Ok(Self::Clickhouse { address, dataset }) + Ok(v4::inventory::OmicronZoneType::Clickhouse { + address, + dataset, + }) } OmicronZoneType::ClickhouseKeeper { address, dataset } => { - Ok(Self::ClickhouseKeeper { address, dataset }) + Ok(v4::inventory::OmicronZoneType::ClickhouseKeeper { + address, + dataset, + }) } OmicronZoneType::ClickhouseServer { address, dataset } => { - Ok(Self::ClickhouseServer { address, dataset }) + Ok(v4::inventory::OmicronZoneType::ClickhouseServer { + address, + dataset, + }) } OmicronZoneType::CockroachDb { address, dataset } => { - Ok(Self::CockroachDb { address, dataset }) + Ok(v4::inventory::OmicronZoneType::CockroachDb { + address, + dataset, + }) } OmicronZoneType::Crucible { address, dataset } => { - Ok(Self::Crucible { address, dataset }) + Ok(v4::inventory::OmicronZoneType::Crucible { + address, + dataset, + }) } OmicronZoneType::CruciblePantry { address } => { - Ok(Self::CruciblePantry { address }) + Ok(v4::inventory::OmicronZoneType::CruciblePantry { address }) } OmicronZoneType::ExternalDns { dataset, http_address, dns_address, nic, - } => Ok(Self::ExternalDns { + } => Ok(v4::inventory::OmicronZoneType::ExternalDns { dataset, http_address, dns_address, @@ -405,7 +429,7 @@ impl TryFrom for v10::OmicronZoneType { dns_address, gz_address, gz_address_index, - } => Ok(Self::InternalDns { + } => Ok(v4::inventory::OmicronZoneType::InternalDns { dataset, http_address, dns_address, @@ -413,7 +437,7 @@ impl TryFrom for v10::OmicronZoneType { gz_address_index, }), OmicronZoneType::InternalNtp { address } => { - Ok(Self::InternalNtp { address }) + Ok(v4::inventory::OmicronZoneType::InternalNtp { address }) } OmicronZoneType::Nexus { internal_address, @@ -422,7 +446,7 @@ impl TryFrom for v10::OmicronZoneType { nic, external_tls, external_dns_servers, - } => Ok(Self::Nexus { + } => Ok(v4::inventory::OmicronZoneType::Nexus { internal_address, lockstep_port, external_ip, @@ -431,25 +455,112 @@ impl TryFrom for v10::OmicronZoneType { external_dns_servers, }), OmicronZoneType::Oximeter { address } => { - Ok(Self::Oximeter { address }) + Ok(v4::inventory::OmicronZoneType::Oximeter { address }) } } } } -impl TryFrom for OmicronZoneType { +impl TryFrom + for v4::inventory::ConfigReconcilerInventory +{ + type Error = external::Error; + + fn try_from(value: ConfigReconcilerInventory) -> Result { + Ok(Self { + last_reconciled_config: value.last_reconciled_config.try_into()?, + external_disks: value.external_disks, + datasets: value.datasets, + orphaned_datasets: value.orphaned_datasets, + zones: value.zones, + boot_partitions: value.boot_partitions, + remove_mupdate_override: value.remove_mupdate_override, + }) + } +} + +impl TryFrom + for v4::inventory::ConfigReconcilerInventoryStatus +{ type Error = external::Error; - fn try_from(value: v10::OmicronZoneType) -> Result { + fn try_from( + value: ConfigReconcilerInventoryStatus, + ) -> Result { match value { - v10::OmicronZoneType::BoundaryNtp { + ConfigReconcilerInventoryStatus::NotYetRun => { + Ok(v4::inventory::ConfigReconcilerInventoryStatus::NotYetRun) + } + ConfigReconcilerInventoryStatus::Running { + config, + started_at, + running_for, + } => Ok(v4::inventory::ConfigReconcilerInventoryStatus::Running { + config: Box::new((*config).try_into()?), + started_at, + running_for, + }), + ConfigReconcilerInventoryStatus::Idle { completed_at, ran_for } => { + Ok(v4::inventory::ConfigReconcilerInventoryStatus::Idle { + completed_at, + ran_for, + }) + } + } + } +} + +impl TryFrom for OmicronSledConfig { + type Error = external::Error; + + fn try_from( + value: v4::inventory::OmicronSledConfig, + ) -> Result { + Ok(Self { + generation: value.generation, + disks: value.disks, + datasets: value.datasets, + zones: value + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?, + remove_mupdate_override: value.remove_mupdate_override, + host_phase_2: value.host_phase_2, + }) + } +} + +impl TryFrom for OmicronZoneConfig { + type Error = external::Error; + + fn try_from( + value: v4::inventory::OmicronZoneConfig, + ) -> Result { + Ok(Self { + id: value.id, + filesystem_pool: value.filesystem_pool, + zone_type: value.zone_type.try_into()?, + image_source: value.image_source, + }) + } +} + +impl TryFrom for OmicronZoneType { + type Error = external::Error; + + fn try_from( + value: v4::inventory::OmicronZoneType, + ) -> Result { + match value { + v4::inventory::OmicronZoneType::BoundaryNtp { address, ntp_servers, dns_servers, domain, nic, snat_cfg, - } => Ok(Self::BoundaryNtp { + } => Ok(OmicronZoneType::BoundaryNtp { address, ntp_servers, dns_servers, @@ -457,59 +568,62 @@ impl TryFrom for OmicronZoneType { nic: nic.try_into()?, snat_cfg, }), - v10::OmicronZoneType::Clickhouse { address, dataset } => { - Ok(Self::Clickhouse { address, dataset }) - } - v10::OmicronZoneType::ClickhouseKeeper { address, dataset } => { - Ok(Self::ClickhouseKeeper { address, dataset }) + v4::inventory::OmicronZoneType::Clickhouse { address, dataset } => { + Ok(OmicronZoneType::Clickhouse { address, dataset }) } - v10::OmicronZoneType::ClickhouseServer { address, dataset } => { - Ok(Self::ClickhouseServer { address, dataset }) - } - v10::OmicronZoneType::CockroachDb { address, dataset } => { - Ok(Self::CockroachDb { address, dataset }) - } - v10::OmicronZoneType::Crucible { address, dataset } => { - Ok(Self::Crucible { address, dataset }) + v4::inventory::OmicronZoneType::ClickhouseKeeper { + address, + dataset, + } => Ok(OmicronZoneType::ClickhouseKeeper { address, dataset }), + v4::inventory::OmicronZoneType::ClickhouseServer { + address, + dataset, + } => Ok(OmicronZoneType::ClickhouseServer { address, dataset }), + v4::inventory::OmicronZoneType::CockroachDb { + address, + dataset, + } => Ok(OmicronZoneType::CockroachDb { address, dataset }), + v4::inventory::OmicronZoneType::Crucible { address, dataset } => { + Ok(OmicronZoneType::Crucible { address, dataset }) } - v10::OmicronZoneType::CruciblePantry { address } => { - Ok(Self::CruciblePantry { address }) + v4::inventory::OmicronZoneType::CruciblePantry { address } => { + Ok(OmicronZoneType::CruciblePantry { address }) } - v10::OmicronZoneType::ExternalDns { + v4::inventory::OmicronZoneType::ExternalDns { dataset, http_address, dns_address, nic, - } => Ok(Self::ExternalDns { + } => Ok(OmicronZoneType::ExternalDns { dataset, http_address, dns_address, nic: nic.try_into()?, }), - v10::OmicronZoneType::InternalDns { + v4::inventory::OmicronZoneType::InternalDns { dataset, http_address, dns_address, gz_address, gz_address_index, - } => Ok(Self::InternalDns { + } => Ok(OmicronZoneType::InternalDns { dataset, http_address, dns_address, gz_address, gz_address_index, }), - v10::OmicronZoneType::InternalNtp { address } => { - Ok(Self::InternalNtp { address }) + v4::inventory::OmicronZoneType::InternalNtp { address } => { + Ok(OmicronZoneType::InternalNtp { address }) } - v10::OmicronZoneType::Nexus { + v4::inventory::OmicronZoneType::Nexus { internal_address, lockstep_port, external_ip, nic, external_tls, external_dns_servers, - } => Ok(Self::Nexus { + } => Ok(OmicronZoneType::Nexus { internal_address, lockstep_port, external_ip, @@ -517,249 +631,26 @@ impl TryFrom for OmicronZoneType { external_tls, external_dns_servers, }), - v10::OmicronZoneType::Oximeter { address } => { - Ok(Self::Oximeter { address }) + v4::inventory::OmicronZoneType::Oximeter { address } => { + Ok(OmicronZoneType::Oximeter { address }) } } } } -/// Describes the last attempt made by the sled-agent-config-reconciler to -/// reconcile the current sled config against the actual state of the sled. -#[derive(Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct ConfigReconcilerInventory { - pub last_reconciled_config: OmicronSledConfig, - pub external_disks: - BTreeMap, - pub datasets: BTreeMap, - pub orphaned_datasets: IdOrdMap, - pub zones: BTreeMap, - pub boot_partitions: BootPartitionContents, - /// The result of removing the mupdate override file on disk. - /// - /// `None` if `remove_mupdate_override` was not provided in the sled config. - pub remove_mupdate_override: Option, -} - -impl TryFrom for ConfigReconcilerInventory { +impl TryFrom for OmicronZonesConfig { type Error = external::Error; fn try_from( - value: v10::ConfigReconcilerInventory, + value: v4::inventory::OmicronZonesConfig, ) -> Result { Ok(Self { - last_reconciled_config: value.last_reconciled_config.try_into()?, - external_disks: value.external_disks, - datasets: value.datasets, - orphaned_datasets: value.orphaned_datasets, - zones: value.zones, - boot_partitions: value.boot_partitions, - remove_mupdate_override: value.remove_mupdate_override, - }) - } -} - -/// Status of the sled-agent-config-reconciler task. -#[derive(Deserialize, Serialize, JsonSchema)] -#[serde(tag = "status", rename_all = "snake_case")] -pub enum ConfigReconcilerInventoryStatus { - /// The reconciler task has not yet run for the first time since sled-agent - /// started. - NotYetRun, - /// The reconciler task is actively running. - Running { - config: Box, - started_at: DateTime, - running_for: Duration, - }, - /// The reconciler task is currently idle, but previously did complete a - /// reconciliation attempt. - /// - /// This variant does not include the `OmicronSledConfig` used in the last - /// attempt, because that's always available via - /// [`ConfigReconcilerInventory::last_reconciled_config`]. - Idle { completed_at: DateTime, ran_for: Duration }, -} - -impl TryFrom - for ConfigReconcilerInventoryStatus -{ - type Error = external::Error; - - fn try_from( - value: v10::ConfigReconcilerInventoryStatus, - ) -> Result { - match value { - v10::ConfigReconcilerInventoryStatus::NotYetRun => { - Ok(Self::NotYetRun) - } - v10::ConfigReconcilerInventoryStatus::Running { - config, - started_at, - running_for, - } => Ok(Self::Running { - config: Box::new((*config).try_into()?), - started_at, - running_for, - }), - v10::ConfigReconcilerInventoryStatus::Idle { - completed_at, - ran_for, - } => Ok(Self::Idle { completed_at, ran_for }), - } - } -} - -/// The body of a request to ensure that a instance and VMM are known to a sled -/// agent. -#[derive(Serialize, Deserialize, JsonSchema)] -pub struct InstanceEnsureBody { - /// The virtual hardware configuration this virtual machine should have when - /// it is started. - pub vmm_spec: VmmSpec, - - /// Information about the sled-local configuration that needs to be - /// established to make the VM's virtual hardware fully functional. - pub local_config: InstanceSledLocalConfig, - - /// The initial VMM runtime state for the VMM being registered. - pub vmm_runtime: VmmRuntimeState, - - /// The ID of the instance for which this VMM is being created. - pub instance_id: InstanceUuid, - - /// The ID of the migration in to this VMM, if this VMM is being - /// ensured is part of a migration in. If this is `None`, the VMM is not - /// being created due to a migration. - pub migration_id: Option, - - /// The address at which this VMM should serve a Propolis server API. - pub propolis_addr: SocketAddr, - - /// Metadata used to track instance statistics. - pub metadata: InstanceMetadata, -} - -impl TryFrom for v10::InstanceEnsureBody { - type Error = external::Error; - - fn try_from(value: InstanceEnsureBody) -> Result { - let local_config = value.local_config.try_into()?; - Ok(Self { - vmm_spec: value.vmm_spec, - local_config, - vmm_runtime: value.vmm_runtime, - instance_id: value.instance_id, - migration_id: value.migration_id, - propolis_addr: value.propolis_addr, - metadata: value.metadata, - }) - } -} - -/// Describes sled-local configuration that a sled-agent must establish to make -/// the instance's virtual hardware fully functional. -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct InstanceSledLocalConfig { - pub hostname: Hostname, - pub nics: Vec, - pub source_nat: SourceNatConfig, - /// Zero or more external IP addresses (either floating or ephemeral), - /// provided to an instance to allow inbound connectivity. - pub ephemeral_ip: Option, - pub floating_ips: Vec, - pub multicast_groups: Vec, - pub firewall_rules: Vec, - pub dhcp_config: DhcpConfig, - pub delegated_zvols: Vec, -} - -impl TryFrom for v10::InstanceSledLocalConfig { - type Error = external::Error; - - fn try_from(value: InstanceSledLocalConfig) -> Result { - let nics = value - .nics - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - let firewall_rules = value - .firewall_rules - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { - hostname: value.hostname, - nics, - source_nat: value.source_nat, - ephemeral_ip: value.ephemeral_ip, - floating_ips: value.floating_ips, - multicast_groups: value.multicast_groups, - firewall_rules, - dhcp_config: value.dhcp_config, - delegated_zvols: value.delegated_zvols, - }) - } -} - -/// VPC firewall rule after object name resolution has been performed by Nexus -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] -pub struct ResolvedVpcFirewallRule { - pub status: external::VpcFirewallRuleStatus, - pub direction: external::VpcFirewallRuleDirection, - pub targets: Vec, - pub filter_hosts: Option>, - pub filter_ports: Option>, - pub filter_protocols: Option>, - pub action: external::VpcFirewallRuleAction, - pub priority: external::VpcFirewallRulePriority, -} - -impl TryFrom - for omicron_common::api::internal::shared::ResolvedVpcFirewallRule -{ - type Error = external::Error; - - fn try_from(value: ResolvedVpcFirewallRule) -> Result { - let targets = value - .targets - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { - status: value.status, - direction: value.direction, - targets, - filter_hosts: value.filter_hosts, - filter_ports: value.filter_ports, - filter_protocols: value.filter_protocols, - action: value.action, - priority: value.priority, + generation: value.generation, + zones: value + .zones + .into_iter() + .map(TryInto::try_into) + .collect::>()?, }) } } - -/// Update firewall rules for a VPC -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct VpcFirewallRulesEnsureBody { - pub vni: external::Vni, - pub rules: Vec, -} - -impl TryFrom - for crate::firewall_rules::VpcFirewallRulesEnsureBody -{ - type Error = external::Error; - - fn try_from( - value: VpcFirewallRulesEnsureBody, - ) -> Result { - let rules = value - .rules - .into_iter() - .map(TryInto::try_into) - .collect::>()?; - Ok(Self { vni: value.vni, rules }) - } -} diff --git a/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/mod.rs b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/mod.rs new file mode 100644 index 00000000000..9081d10bea5 --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/mod.rs @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `ADD_DUAL_STACK_SHARED_NETWORK_INTERFACES` of the Sled Agent API. +//! +//! This version added dual-stack support to NetworkInterface, changing +//! from a single IP address to `PrivateIpConfig` which can hold both +//! IPv4 and IPv6 addresses. +//! +//! All types in this version use `NetworkInterface` v2 (dual-stack). + +pub mod firewall_rules; +pub mod instance; +pub mod inventory; +pub mod probes; diff --git a/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/probes.rs b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/probes.rs new file mode 100644 index 00000000000..585582b7e82 --- /dev/null +++ b/sled-agent/types/versions/src/add_dual_stack_shared_network_interfaces/probes.rs @@ -0,0 +1,110 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use crate::v6; +use iddqd::IdHashItem; +use iddqd::IdHashMap; +use iddqd::id_upcast; +use omicron_common::api::external::Error as ExternalError; +use omicron_common::api::internal::shared::NetworkInterface; +use omicron_uuid_kinds::ProbeUuid; +use schemars::JsonSchema; +use serde::Deserialize; +use serde::Serialize; +use std::net::IpAddr; + +/// Parameters used to create a probe. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ProbeCreate { + /// The ID for the probe. + pub id: ProbeUuid, + /// The external IP addresses assigned to the probe. + pub external_ips: Vec, + /// The probe's networking interface. + pub interface: NetworkInterface, +} + +impl IdHashItem for ProbeCreate { + type Key<'a> = ProbeUuid; + + fn key(&self) -> Self::Key<'_> { + self.id + } + + id_upcast!(); +} + +/// An external IP address used by a probe. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ExternalIp { + /// The external IP address. + pub ip: IpAddr, + /// The kind of address this is. + pub kind: IpKind, + /// The first port used by the address. + pub first_port: u16, + /// The last port used by the address. + pub last_port: u16, +} + +/// The kind of external IP address of a probe. +#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum IpKind { + Snat, + Ephemeral, + Floating, +} + +/// A set of probes that the target sled should run. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ProbeSet { + /// The exact set of probes to run. + pub probes: IdHashMap, +} + +impl TryFrom for ProbeCreate { + type Error = ExternalError; + + fn try_from(v6: v6::probes::ProbeCreate) -> Result { + Ok(Self { + id: v6.id, + external_ips: v6.external_ips.into_iter().map(Into::into).collect(), + interface: v6.interface.try_into()?, + }) + } +} + +impl From for ExternalIp { + fn from(v6: v6::probes::ExternalIp) -> Self { + Self { + ip: v6.ip, + kind: v6.kind.into(), + first_port: v6.first_port, + last_port: v6.last_port, + } + } +} + +impl From for IpKind { + fn from(v6: v6::probes::IpKind) -> Self { + match v6 { + v6::probes::IpKind::Snat => Self::Snat, + v6::probes::IpKind::Ephemeral => Self::Ephemeral, + v6::probes::IpKind::Floating => Self::Floating, + } + } +} + +impl TryFrom for ProbeSet { + type Error = ExternalError; + + fn try_from(v6: v6::probes::ProbeSet) -> Result { + v6.probes + .into_iter() + .map(TryInto::try_into) + .collect::>() + .map(|probes| Self { probes }) + } +} diff --git a/sled-agent/api/src/v3.rs b/sled-agent/types/versions/src/add_nexus_lockstep_port_to_inventory/inventory.rs similarity index 74% rename from sled-agent/api/src/v3.rs rename to sled-agent/types/versions/src/add_nexus_lockstep_port_to_inventory/inventory.rs index 7bcd8404d90..140aa92c4b2 100644 --- a/sled-agent/api/src/v3.rs +++ b/sled-agent/types/versions/src/add_nexus_lockstep_port_to_inventory/inventory.rs @@ -2,45 +2,36 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use chrono::DateTime; -use chrono::Utc; +use std::collections::BTreeMap; +use std::net::{IpAddr, Ipv6Addr, SocketAddr, SocketAddrV6}; +use std::time::Duration; + +use chrono::{DateTime, Utc}; use iddqd::IdOrdItem; use iddqd::IdOrdMap; use iddqd::id_upcast; -use nexus_sled_agent_shared::inventory::{ - BootPartitionContents, ConfigReconcilerInventoryResult, - HostPhase2DesiredSlots, InventoryDataset, InventoryDisk, InventoryZpool, - OmicronZoneDataset, OmicronZoneImageSource, OrphanedDataset, - RemoveMupdateOverrideInventory, SledRole, ZoneImageResolverInventory, -}; use omicron_common::address::NEXUS_LOCKSTEP_PORT; -use omicron_common::api::external::ByteCount; -use omicron_common::api::external::Generation; +use omicron_common::api::external::{ByteCount, Generation}; use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; -use omicron_common::disk::DatasetConfig; -use omicron_common::disk::OmicronPhysicalDiskConfig; +use omicron_common::disk::{DatasetConfig, OmicronPhysicalDiskConfig}; use omicron_common::zpool_name::ZpoolName; -use omicron_uuid_kinds::DatasetUuid; -use omicron_uuid_kinds::MupdateOverrideUuid; -use omicron_uuid_kinds::OmicronZoneUuid; -use omicron_uuid_kinds::PhysicalDiskUuid; -use omicron_uuid_kinds::SledUuid; +use omicron_uuid_kinds::{DatasetUuid, MupdateOverrideUuid, OmicronZoneUuid}; +use omicron_uuid_kinds::{PhysicalDiskUuid, SledUuid}; use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use sled_agent_types::inventory::v9; -use sled_hardware_types::Baseboard; -use sled_hardware_types::SledCpuFamily; -use std::collections::BTreeMap; -use std::net::IpAddr; -use std::net::Ipv6Addr; -use std::net::SocketAddr; -use std::net::SocketAddrV6; -use std::time::Duration; +use serde::{Deserialize, Serialize}; + +use crate::v1; +use crate::v1::inventory::{ + BootPartitionContents, ConfigReconcilerInventoryResult, + HostPhase2DesiredSlots, InventoryDataset, InventoryDisk, InventoryZpool, + OmicronZoneDataset, OmicronZoneImageSource, OrphanedDataset, + RemoveMupdateOverrideInventory, SledRole, ZoneImageResolverInventory, +}; +use sled_hardware_types::{Baseboard, SledCpuFamily}; /// Identity and basic status information about this sled agent -#[derive(Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] pub struct Inventory { pub sled_id: SledUuid, pub sled_agent_address: SocketAddrV6, @@ -59,49 +50,12 @@ pub struct Inventory { pub zone_image_resolver: ZoneImageResolverInventory, } -impl From for Inventory { - fn from(value: v9::Inventory) -> Self { - let v9::Inventory { - sled_id, - sled_agent_address, - sled_role, - baseboard, - usable_hardware_threads, - usable_physical_ram, - cpu_family, - reservoir_size, - disks, - zpools, - datasets, - ledgered_sled_config, - reconciler_status, - last_reconciliation, - zone_image_resolver, - } = value; - Self { - sled_id, - sled_agent_address, - sled_role, - baseboard, - usable_hardware_threads, - usable_physical_ram, - cpu_family, - reservoir_size, - disks, - zpools, - datasets, - ledgered_sled_config: ledgered_sled_config.map(Into::into), - reconciler_status: reconciler_status.into(), - last_reconciliation: last_reconciliation.map(Into::into), - zone_image_resolver, - } - } -} - /// Describes the set of Reconfigurator-managed configuration elements of a sled -#[derive(Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] pub struct OmicronSledConfig { pub generation: Generation, + // Serialize and deserialize disks, datasets, and zones as maps for + // backwards compatibility. Newer IdOrdMaps should not use IdOrdMapAsMap. #[serde( with = "iddqd::id_ord_map::IdOrdMapAsMap::" )] @@ -115,34 +69,10 @@ pub struct OmicronSledConfig { pub host_phase_2: HostPhase2DesiredSlots, } -impl From for v9::OmicronSledConfig { - fn from(value: OmicronSledConfig) -> Self { - Self { - generation: value.generation, - disks: value.disks, - datasets: value.datasets, - zones: value.zones.into_iter().map(Into::into).collect(), - remove_mupdate_override: value.remove_mupdate_override, - host_phase_2: value.host_phase_2, - } - } -} - -impl From for OmicronSledConfig { - fn from(value: v9::OmicronSledConfig) -> Self { - Self { - generation: value.generation, - disks: value.disks, - datasets: value.datasets, - zones: value.zones.into_iter().map(Into::into).collect(), - remove_mupdate_override: value.remove_mupdate_override, - host_phase_2: value.host_phase_2, - } - } -} - /// Describes one Omicron-managed zone running on a sled -#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] pub struct OmicronZoneConfig { pub id: OmicronZoneUuid, @@ -168,31 +98,11 @@ impl IdOrdItem for OmicronZoneConfig { id_upcast!(); } -impl From for v9::OmicronZoneConfig { - fn from(value: OmicronZoneConfig) -> Self { - Self { - id: value.id, - filesystem_pool: value.filesystem_pool, - zone_type: value.zone_type.into(), - image_source: value.image_source, - } - } -} - -impl From for OmicronZoneConfig { - fn from(value: v9::OmicronZoneConfig) -> Self { - Self { - id: value.id, - filesystem_pool: value.filesystem_pool, - zone_type: value.zone_type.into(), - image_source: value.image_source, - } - } -} - /// Describes what kind of zone this is (i.e., what component is running in it) /// as well as any type-specific configuration -#[derive(Debug, Deserialize, Serialize, JsonSchema)] +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] #[serde(tag = "type", rename_all = "snake_case")] pub enum OmicronZoneType { BoundaryNtp { @@ -269,6 +179,10 @@ pub enum OmicronZoneType { Nexus { /// The address at which the internal nexus server is reachable. internal_address: SocketAddrV6, + /// The port at which the internal lockstep server is reachable. This + /// shares the same IP address with `internal_address`. + #[serde(default = "default_nexus_lockstep_port")] + lockstep_port: u16, /// The address at which the external nexus server is reachable. external_ip: IpAddr, /// The service vNIC providing external connectivity using OPTE. @@ -283,10 +197,97 @@ pub enum OmicronZoneType { }, } -impl From for v9::OmicronZoneType { - fn from(value: OmicronZoneType) -> Self { +fn default_nexus_lockstep_port() -> u16 { + omicron_common::address::NEXUS_LOCKSTEP_PORT +} + +/// Describes the last attempt made by the sled-agent-config-reconciler to +/// reconcile the current sled config against the actual state of the sled. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct ConfigReconcilerInventory { + pub last_reconciled_config: OmicronSledConfig, + pub external_disks: + BTreeMap, + pub datasets: BTreeMap, + pub orphaned_datasets: IdOrdMap, + pub zones: BTreeMap, + pub boot_partitions: BootPartitionContents, + /// The result of removing the mupdate override file on disk. + /// + /// `None` if `remove_mupdate_override` was not provided in the sled config. + pub remove_mupdate_override: Option, +} + +/// Status of the sled-agent-config-reconciler task. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(tag = "status", rename_all = "snake_case")] +pub enum ConfigReconcilerInventoryStatus { + /// The reconciler task has not yet run for the first time since sled-agent + /// started. + NotYetRun, + /// The reconciler task is actively running. + Running { + config: Box, + started_at: DateTime, + running_for: Duration, + }, + /// The reconciler task is currently idle, but previously did complete a + /// reconciliation attempt. + /// + /// This variant does not include the `OmicronSledConfig` used in the last + /// attempt, because that's always available via + /// [`ConfigReconcilerInventory::last_reconciled_config`]. + Idle { completed_at: DateTime, ran_for: Duration }, +} + +/// Describes the set of Omicron-managed zones running on a sled +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +pub struct OmicronZonesConfig { + /// generation number of this configuration + /// + /// This generation number is owned by the control plane (i.e., RSS or + /// Nexus, depending on whether RSS-to-Nexus handoff has happened). It + /// should not be bumped within Sled Agent. + /// + /// Sled Agent rejects attempts to set the configuration to a generation + /// older than the one it's currently running. + pub generation: Generation, + + /// list of running zones + pub zones: Vec, +} + +impl From for OmicronSledConfig { + fn from(value: v1::inventory::OmicronSledConfig) -> Self { + Self { + generation: value.generation, + disks: value.disks, + datasets: value.datasets, + zones: value.zones.into_iter().map(Into::into).collect(), + remove_mupdate_override: value.remove_mupdate_override, + host_phase_2: value.host_phase_2, + } + } +} + +impl From for OmicronZoneConfig { + fn from(value: v1::inventory::OmicronZoneConfig) -> Self { + Self { + id: value.id, + filesystem_pool: value.filesystem_pool, + zone_type: value.zone_type.into(), + image_source: value.image_source, + } + } +} + +impl From for OmicronZoneType { + fn from(value: v1::inventory::OmicronZoneType) -> Self { match value { - OmicronZoneType::BoundaryNtp { + v1::inventory::OmicronZoneType::BoundaryNtp { address, ntp_servers, dns_servers, @@ -301,31 +302,34 @@ impl From for v9::OmicronZoneType { nic, snat_cfg, }, - OmicronZoneType::Clickhouse { address, dataset } => { + v1::inventory::OmicronZoneType::Clickhouse { address, dataset } => { Self::Clickhouse { address, dataset } } - OmicronZoneType::ClickhouseKeeper { address, dataset } => { - Self::ClickhouseKeeper { address, dataset } - } - OmicronZoneType::ClickhouseServer { address, dataset } => { - Self::ClickhouseServer { address, dataset } - } - OmicronZoneType::CockroachDb { address, dataset } => { - Self::CockroachDb { address, dataset } - } - OmicronZoneType::Crucible { address, dataset } => { + v1::inventory::OmicronZoneType::ClickhouseKeeper { + address, + dataset, + } => Self::ClickhouseKeeper { address, dataset }, + v1::inventory::OmicronZoneType::ClickhouseServer { + address, + dataset, + } => Self::ClickhouseServer { address, dataset }, + v1::inventory::OmicronZoneType::CockroachDb { + address, + dataset, + } => Self::CockroachDb { address, dataset }, + v1::inventory::OmicronZoneType::Crucible { address, dataset } => { Self::Crucible { address, dataset } } - OmicronZoneType::CruciblePantry { address } => { + v1::inventory::OmicronZoneType::CruciblePantry { address } => { Self::CruciblePantry { address } } - OmicronZoneType::ExternalDns { + v1::inventory::OmicronZoneType::ExternalDns { dataset, http_address, dns_address, nic, } => Self::ExternalDns { dataset, http_address, dns_address, nic }, - OmicronZoneType::InternalDns { + v1::inventory::OmicronZoneType::InternalDns { dataset, http_address, dns_address, @@ -338,10 +342,10 @@ impl From for v9::OmicronZoneType { gz_address, gz_address_index, }, - OmicronZoneType::InternalNtp { address } => { + v1::inventory::OmicronZoneType::InternalNtp { address } => { Self::InternalNtp { address } } - OmicronZoneType::Nexus { + v1::inventory::OmicronZoneType::Nexus { internal_address, external_ip, nic, @@ -349,21 +353,69 @@ impl From for v9::OmicronZoneType { external_dns_servers, } => Self::Nexus { internal_address, - lockstep_port: NEXUS_LOCKSTEP_PORT, + lockstep_port: NEXUS_LOCKSTEP_PORT, // Added with default external_ip, nic, external_tls, external_dns_servers, }, - OmicronZoneType::Oximeter { address } => Self::Oximeter { address }, + v1::inventory::OmicronZoneType::Oximeter { address } => { + Self::Oximeter { address } + } + } + } +} + +impl From for v1::inventory::Inventory { + fn from(value: Inventory) -> Self { + Self { + sled_id: value.sled_id, + sled_agent_address: value.sled_agent_address, + sled_role: value.sled_role, + baseboard: value.baseboard, + usable_hardware_threads: value.usable_hardware_threads, + usable_physical_ram: value.usable_physical_ram, + cpu_family: value.cpu_family, + reservoir_size: value.reservoir_size, + disks: value.disks, + zpools: value.zpools, + datasets: value.datasets, + ledgered_sled_config: value.ledgered_sled_config.map(Into::into), + reconciler_status: value.reconciler_status.into(), + last_reconciliation: value.last_reconciliation.map(Into::into), + zone_image_resolver: value.zone_image_resolver, + } + } +} + +impl From for v1::inventory::OmicronSledConfig { + fn from(value: OmicronSledConfig) -> Self { + Self { + generation: value.generation, + disks: value.disks, + datasets: value.datasets, + zones: value.zones.into_iter().map(Into::into).collect(), + remove_mupdate_override: value.remove_mupdate_override, + host_phase_2: value.host_phase_2, } } } -impl From for OmicronZoneType { - fn from(value: v9::OmicronZoneType) -> Self { +impl From for v1::inventory::OmicronZoneConfig { + fn from(value: OmicronZoneConfig) -> Self { + Self { + id: value.id, + filesystem_pool: value.filesystem_pool, + zone_type: value.zone_type.into(), + image_source: value.image_source, + } + } +} + +impl From for v1::inventory::OmicronZoneType { + fn from(value: OmicronZoneType) -> Self { match value { - v9::OmicronZoneType::BoundaryNtp { + OmicronZoneType::BoundaryNtp { address, ntp_servers, dns_servers, @@ -378,31 +430,31 @@ impl From for OmicronZoneType { nic, snat_cfg, }, - v9::OmicronZoneType::Clickhouse { address, dataset } => { + OmicronZoneType::Clickhouse { address, dataset } => { Self::Clickhouse { address, dataset } } - v9::OmicronZoneType::ClickhouseKeeper { address, dataset } => { + OmicronZoneType::ClickhouseKeeper { address, dataset } => { Self::ClickhouseKeeper { address, dataset } } - v9::OmicronZoneType::ClickhouseServer { address, dataset } => { + OmicronZoneType::ClickhouseServer { address, dataset } => { Self::ClickhouseServer { address, dataset } } - v9::OmicronZoneType::CockroachDb { address, dataset } => { + OmicronZoneType::CockroachDb { address, dataset } => { Self::CockroachDb { address, dataset } } - v9::OmicronZoneType::Crucible { address, dataset } => { + OmicronZoneType::Crucible { address, dataset } => { Self::Crucible { address, dataset } } - v9::OmicronZoneType::CruciblePantry { address } => { + OmicronZoneType::CruciblePantry { address } => { Self::CruciblePantry { address } } - v9::OmicronZoneType::ExternalDns { + OmicronZoneType::ExternalDns { dataset, http_address, dns_address, nic, } => Self::ExternalDns { dataset, http_address, dns_address, nic }, - v9::OmicronZoneType::InternalDns { + OmicronZoneType::InternalDns { dataset, http_address, dns_address, @@ -415,16 +467,16 @@ impl From for OmicronZoneType { gz_address, gz_address_index, }, - v9::OmicronZoneType::InternalNtp { address } => { + OmicronZoneType::InternalNtp { address } => { Self::InternalNtp { address } } - v9::OmicronZoneType::Nexus { + OmicronZoneType::Nexus { internal_address, + lockstep_port: _, // Dropped in v1 external_ip, nic, external_tls, external_dns_servers, - lockstep_port: _, } => Self::Nexus { internal_address, external_ip, @@ -432,32 +484,14 @@ impl From for OmicronZoneType { external_tls, external_dns_servers, }, - v9::OmicronZoneType::Oximeter { address } => { - Self::Oximeter { address } - } + OmicronZoneType::Oximeter { address } => Self::Oximeter { address }, } } } -/// Describes the last attempt made by the sled-agent-config-reconciler to -/// reconcile the current sled config against the actual state of the sled. -#[derive(Deserialize, Serialize, JsonSchema)] -#[serde(rename_all = "snake_case")] -pub struct ConfigReconcilerInventory { - pub last_reconciled_config: OmicronSledConfig, - pub external_disks: - BTreeMap, - pub datasets: BTreeMap, - pub orphaned_datasets: IdOrdMap, - pub zones: BTreeMap, - pub boot_partitions: BootPartitionContents, - /// The result of removing the mupdate override file on disk. - /// - /// `None` if `remove_mupdate_override` was not provided in the sled config. - pub remove_mupdate_override: Option, -} - -impl From for v9::ConfigReconcilerInventory { +impl From + for v1::inventory::ConfigReconcilerInventory +{ fn from(value: ConfigReconcilerInventory) -> Self { Self { last_reconciled_config: value.last_reconciled_config.into(), @@ -471,44 +505,8 @@ impl From for v9::ConfigReconcilerInventory { } } -impl From for ConfigReconcilerInventory { - fn from(value: v9::ConfigReconcilerInventory) -> Self { - Self { - last_reconciled_config: value.last_reconciled_config.into(), - external_disks: value.external_disks, - datasets: value.datasets, - orphaned_datasets: value.orphaned_datasets, - zones: value.zones, - boot_partitions: value.boot_partitions, - remove_mupdate_override: value.remove_mupdate_override, - } - } -} - -/// Status of the sled-agent-config-reconciler task. -#[derive(Deserialize, Serialize, JsonSchema)] -#[serde(tag = "status", rename_all = "snake_case")] -pub enum ConfigReconcilerInventoryStatus { - /// The reconciler task has not yet run for the first time since sled-agent - /// started. - NotYetRun, - /// The reconciler task is actively running. - Running { - config: Box, - started_at: DateTime, - running_for: Duration, - }, - /// The reconciler task is currently idle, but previously did complete a - /// reconciliation attempt. - /// - /// This variant does not include the `OmicronSledConfig` used in the last - /// attempt, because that's always available via - /// [`ConfigReconcilerInventory::last_reconciled_config`]. - Idle { completed_at: DateTime, ran_for: Duration }, -} - impl From - for v9::ConfigReconcilerInventoryStatus + for v1::inventory::ConfigReconcilerInventoryStatus { fn from(value: ConfigReconcilerInventoryStatus) -> Self { match value { @@ -528,26 +526,3 @@ impl From } } } - -impl From - for ConfigReconcilerInventoryStatus -{ - fn from(value: v9::ConfigReconcilerInventoryStatus) -> Self { - match value { - v9::ConfigReconcilerInventoryStatus::NotYetRun => Self::NotYetRun, - v9::ConfigReconcilerInventoryStatus::Running { - config, - started_at, - running_for, - } => Self::Running { - config: Box::new((*config).into()), - started_at, - running_for, - }, - v9::ConfigReconcilerInventoryStatus::Idle { - completed_at, - ran_for, - } => Self::Idle { completed_at, ran_for }, - } - } -} diff --git a/sled-agent/types/versions/src/add_nexus_lockstep_port_to_inventory/mod.rs b/sled-agent/types/versions/src/add_nexus_lockstep_port_to_inventory/mod.rs new file mode 100644 index 00000000000..8c8f73e631c --- /dev/null +++ b/sled-agent/types/versions/src/add_nexus_lockstep_port_to_inventory/mod.rs @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `ADD_NEXUS_LOCKSTEP_PORT_TO_INVENTORY` of the Sled Agent API. +//! +//! This version added `lockstep_port` to the Nexus zone type. + +pub mod inventory; diff --git a/sled-agent/types/versions/src/add_probe_put_endpoint/mod.rs b/sled-agent/types/versions/src/add_probe_put_endpoint/mod.rs new file mode 100644 index 00000000000..63300d9b0b2 --- /dev/null +++ b/sled-agent/types/versions/src/add_probe_put_endpoint/mod.rs @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `ADD_PROBE_PUT_ENDPOINT` of the Sled Agent API. +//! +//! This version introduced the probe PUT endpoint. + +pub mod probes; diff --git a/sled-agent/types/src/probes/mod.rs b/sled-agent/types/versions/src/add_probe_put_endpoint/probes.rs similarity index 91% rename from sled-agent/types/src/probes/mod.rs rename to sled-agent/types/versions/src/add_probe_put_endpoint/probes.rs index 203d05665b4..15c410f3df0 100644 --- a/sled-agent/types/src/probes/mod.rs +++ b/sled-agent/types/versions/src/add_probe_put_endpoint/probes.rs @@ -2,21 +2,16 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Types for manipulating networking probe zones. - use iddqd::IdHashItem; use iddqd::IdHashMap; use iddqd::id_upcast; -use omicron_common::api::internal::shared::NetworkInterface; +use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; use omicron_uuid_kinds::ProbeUuid; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use std::net::IpAddr; -/// Version 1 of the probe API types. -pub mod v1; - /// Parameters used to create a probe. #[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] pub struct ProbeCreate { diff --git a/sled-agent/types/versions/src/add_switch_zone_operator_policy/debug.rs b/sled-agent/types/versions/src/add_switch_zone_operator_policy/debug.rs new file mode 100644 index 00000000000..144195bedc5 --- /dev/null +++ b/sled-agent/types/versions/src/add_switch_zone_operator_policy/debug.rs @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Policy allowing an operator (via `omdb`) to control whether the switch zone +/// is started or stopped. +/// +/// This is an _extremely_ dicey operation in general; a stopped switch zone +/// leaves the rack inoperable! We are only adding this as a workaround and test +/// tool for handling sidecar resets; see +/// for background. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema, +)] +#[serde(tag = "policy", rename_all = "snake_case")] +pub enum OperatorSwitchZonePolicy { + /// Start the switch zone if a switch is present. + /// + /// This is the default policy. + StartIfSwitchPresent, + + /// Even if a switch zone is present, stop the switch zone. + StopDespiteSwitchPresence, +} diff --git a/sled-agent/types/versions/src/add_switch_zone_operator_policy/mod.rs b/sled-agent/types/versions/src/add_switch_zone_operator_policy/mod.rs new file mode 100644 index 00000000000..9fc959ca481 --- /dev/null +++ b/sled-agent/types/versions/src/add_switch_zone_operator_policy/mod.rs @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `ADD_SWITCH_ZONE_OPERATOR_POLICY` of the Sled Agent API. +//! +//! This version added the operator switch zone policy debug endpoint. + +pub mod debug; diff --git a/sled-agent/types/versions/src/bootstrap_initial/mod.rs b/sled-agent/types/versions/src/bootstrap_initial/mod.rs new file mode 100644 index 00000000000..925f5ae67ef --- /dev/null +++ b/sled-agent/types/versions/src/bootstrap_initial/mod.rs @@ -0,0 +1,10 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `INITIAL` of the Bootstrap Agent API. +//! +//! These types are used by the bootstrap-agent API and are shared between +//! Nexus and sled-agent during rack setup. + +pub mod rack_init; diff --git a/nexus-sled-agent-shared/src/recovery_silo.rs b/sled-agent/types/versions/src/bootstrap_initial/rack_init.rs similarity index 89% rename from nexus-sled-agent-shared/src/recovery_silo.rs rename to sled-agent/types/versions/src/bootstrap_initial/rack_init.rs index e9e70a1f654..94ca4c95c6c 100644 --- a/nexus-sled-agent-shared/src/recovery_silo.rs +++ b/sled-agent/types/versions/src/bootstrap_initial/rack_init.rs @@ -2,12 +2,11 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Configuration for the recovery silo. - use omicron_common::api::external::{Name, UserId}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; +/// Configuration for the recovery silo created during rack setup. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct RecoverySiloConfig { pub silo_name: Name, diff --git a/sled-agent/types/versions/src/delegate_zvol_to_propolis/dataset.rs b/sled-agent/types/versions/src/delegate_zvol_to_propolis/dataset.rs new file mode 100644 index 00000000000..6770c17b52a --- /dev/null +++ b/sled-agent/types/versions/src/delegate_zvol_to_propolis/dataset.rs @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use omicron_common::api::external::ByteCount; +use omicron_uuid_kinds::{DatasetUuid, ExternalZpoolUuid}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Path parameters for Local Storage dataset related requests. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct LocalStoragePathParam { + pub zpool_id: ExternalZpoolUuid, + pub dataset_id: DatasetUuid, +} + +/// Dataset and Volume details for a Local Storage dataset ensure request. +#[derive(Clone, Serialize, Deserialize, JsonSchema)] +pub struct LocalStorageDatasetEnsureRequest { + /// Size of the parent dataset + pub dataset_size: ByteCount, + + /// Size of the zvol + pub volume_size: ByteCount, +} diff --git a/sled-agent/types/versions/src/delegate_zvol_to_propolis/firewall_rules.rs b/sled-agent/types/versions/src/delegate_zvol_to_propolis/firewall_rules.rs new file mode 100644 index 00000000000..07aaa064e4d --- /dev/null +++ b/sled-agent/types/versions/src/delegate_zvol_to_propolis/firewall_rules.rs @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Firewall rule types added in version `DELEGATE_ZVOL_TO_PROPOLIS`. + +use omicron_common::api::external; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::v1::instance::ResolvedVpcFirewallRule; + +/// Update firewall rules for a VPC +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct VpcFirewallRulesEnsureBody { + pub vni: external::Vni, + pub rules: Vec, +} diff --git a/sled-agent/types/versions/src/delegate_zvol_to_propolis/instance.rs b/sled-agent/types/versions/src/delegate_zvol_to_propolis/instance.rs new file mode 100644 index 00000000000..162a71870da --- /dev/null +++ b/sled-agent/types/versions/src/delegate_zvol_to_propolis/instance.rs @@ -0,0 +1,100 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::net::{IpAddr, SocketAddr}; + +use omicron_common::api::external::Hostname; +use omicron_common::api::internal::nexus::VmmRuntimeState; +use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; +use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; +use omicron_common::api::internal::shared::{DelegatedZvol, DhcpConfig}; +use omicron_uuid_kinds::InstanceUuid; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +use crate::v1::instance::InstanceMetadata; +use crate::v1::instance::ResolvedVpcFirewallRule; +use crate::v1::instance::VmmSpec; +use crate::v7; +use crate::v7::instance::InstanceMulticastMembership; + +/// The body of a request to ensure that a instance and VMM are known to a sled +/// agent. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct InstanceEnsureBody { + /// The virtual hardware configuration this virtual machine should have when + /// it is started. + pub vmm_spec: VmmSpec, + + /// Information about the sled-local configuration that needs to be + /// established to make the VM's virtual hardware fully functional. + pub local_config: InstanceSledLocalConfig, + + /// The initial VMM runtime state for the VMM being registered. + pub vmm_runtime: VmmRuntimeState, + + /// The ID of the instance for which this VMM is being created. + pub instance_id: InstanceUuid, + + /// The ID of the migration in to this VMM, if this VMM is being + /// ensured is part of a migration in. If this is `None`, the VMM is not + /// being created due to a migration. + pub migration_id: Option, + + /// The address at which this VMM should serve a Propolis server API. + pub propolis_addr: SocketAddr, + + /// Metadata used to track instance statistics. + pub metadata: InstanceMetadata, +} + +/// Describes sled-local configuration that a sled-agent must establish to make +/// the instance's virtual hardware fully functional. +/// +/// Added in v9: `delegated_zvols` field. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct InstanceSledLocalConfig { + pub hostname: Hostname, + pub nics: Vec, + pub source_nat: SourceNatConfig, + /// Zero or more external IP addresses (either floating or ephemeral), + /// provided to an instance to allow inbound connectivity. + pub ephemeral_ip: Option, + pub floating_ips: Vec, + pub multicast_groups: Vec, + pub firewall_rules: Vec, + pub dhcp_config: DhcpConfig, + pub delegated_zvols: Vec, +} + +impl From for InstanceEnsureBody { + fn from(v7: v7::instance::InstanceEnsureBody) -> Self { + Self { + vmm_spec: v7.vmm_spec, + local_config: v7.local_config.into(), + vmm_runtime: v7.vmm_runtime, + instance_id: v7.instance_id, + migration_id: v7.migration_id, + propolis_addr: v7.propolis_addr, + metadata: v7.metadata, + } + } +} + +impl From for InstanceSledLocalConfig { + fn from(v7: v7::instance::InstanceSledLocalConfig) -> Self { + Self { + hostname: v7.hostname, + nics: v7.nics, + source_nat: v7.source_nat, + ephemeral_ip: v7.ephemeral_ip, + floating_ips: v7.floating_ips, + multicast_groups: v7.multicast_groups, + firewall_rules: v7.firewall_rules, + dhcp_config: v7.dhcp_config, + delegated_zvols: Vec::new(), // Added in v9 + } + } +} diff --git a/sled-agent/types/versions/src/delegate_zvol_to_propolis/mod.rs b/sled-agent/types/versions/src/delegate_zvol_to_propolis/mod.rs new file mode 100644 index 00000000000..2e9bc097fc9 --- /dev/null +++ b/sled-agent/types/versions/src/delegate_zvol_to_propolis/mod.rs @@ -0,0 +1,11 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `DELEGATE_ZVOL_TO_PROPOLIS` of the Sled Agent API. +//! +//! This version added support for delegating zvols to Propolis. + +pub mod dataset; +pub mod firewall_rules; +pub mod instance; diff --git a/sled-agent/types/versions/src/initial/artifact.rs b/sled-agent/types/versions/src/initial/artifact.rs new file mode 100644 index 00000000000..1aad7a89fef --- /dev/null +++ b/sled-agent/types/versions/src/initial/artifact.rs @@ -0,0 +1,72 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Artifact types for Sled Agent API v1. + +use std::collections::{BTreeMap, BTreeSet}; + +use omicron_common::api::external::Generation; +use omicron_common::ledger::Ledgerable; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use tufaceous_artifact::ArtifactHash; + +/// Path parameters for Artifact requests. +#[derive(Deserialize, JsonSchema)] +pub struct ArtifactPathParam { + pub sha256: ArtifactHash, +} + +/// Query parameters for artifact requests. +#[derive(Deserialize, JsonSchema)] +pub struct ArtifactQueryParam { + pub generation: Generation, +} + +/// Request body for copying artifacts from a depot. +#[derive(Deserialize, JsonSchema)] +pub struct ArtifactCopyFromDepotBody { + pub depot_base_url: String, +} + +/// Response for listing artifacts. +#[derive(Debug, Serialize, JsonSchema)] +pub struct ArtifactListResponse { + pub generation: Generation, + pub list: BTreeMap, +} + +/// Response for copying artifacts from a depot. +#[derive(Debug, Serialize, JsonSchema)] +pub struct ArtifactCopyFromDepotResponse {} + +/// Response for putting an artifact. +#[derive(Debug, Serialize, JsonSchema)] +pub struct ArtifactPutResponse { + /// The number of valid M.2 artifact datasets we found on the sled. There is + /// typically one of these datasets for each functional M.2. + pub datasets: usize, + + /// The number of valid writes to the M.2 artifact datasets. This should be + /// less than or equal to the number of artifact datasets. + pub successful_writes: usize, +} + +/// Artifact configuration. +/// +/// This type is used in both GET (response) and PUT (request) operations. +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +pub struct ArtifactConfig { + pub generation: Generation, + pub artifacts: BTreeSet, +} + +impl Ledgerable for ArtifactConfig { + fn is_newer_than(&self, other: &ArtifactConfig) -> bool { + self.generation > other.generation + } + + // No need to do this, the generation number is provided externally. + fn generation_bump(&mut self) {} +} diff --git a/sled-agent/types/versions/src/initial/bootstore.rs b/sled-agent/types/versions/src/initial/bootstore.rs new file mode 100644 index 00000000000..f140891f852 --- /dev/null +++ b/sled-agent/types/versions/src/initial/bootstore.rs @@ -0,0 +1,56 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Bootstore types for the Sled Agent API. + +use std::collections::BTreeSet; +use std::net::SocketAddrV6; + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use sled_hardware_types::Baseboard; + +/// Status of the local bootstore node. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct BootstoreStatus { + pub fsm_ledger_generation: u64, + pub network_config_ledger_generation: Option, + pub fsm_state: String, + pub peers: BTreeSet, + pub established_connections: Vec, + pub accepted_connections: BTreeSet, + pub negotiating_connections: BTreeSet, +} + +impl From for BootstoreStatus { + fn from(value: bootstore::schemes::v0::Status) -> Self { + BootstoreStatus { + fsm_ledger_generation: value.fsm_ledger_generation, + network_config_ledger_generation: value + .network_config_ledger_generation, + fsm_state: value.fsm_state.to_string(), + peers: value.peers, + established_connections: value + .connections + .into_iter() + .map(EstablishedConnection::from) + .collect(), + accepted_connections: value.accepted_connections, + negotiating_connections: value.negotiating_connections, + } + } +} + +/// An established connection to a bootstore peer. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct EstablishedConnection { + pub baseboard: Baseboard, + pub addr: SocketAddrV6, +} + +impl From<(Baseboard, SocketAddrV6)> for EstablishedConnection { + fn from(value: (Baseboard, SocketAddrV6)) -> Self { + EstablishedConnection { baseboard: value.0, addr: value.1 } + } +} diff --git a/sled-agent/types/versions/src/initial/debug.rs b/sled-agent/types/versions/src/initial/debug.rs new file mode 100644 index 00000000000..e5f03c08391 --- /dev/null +++ b/sled-agent/types/versions/src/initial/debug.rs @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Debug/chicken-switch types for Sled Agent API v1. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Chicken switch for orphaned dataset destruction. +/// +/// This type is used in both GET and PUT operations (in versions 1-2). +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ChickenSwitchDestroyOrphanedDatasets { + /// If true, sled-agent will attempt to destroy durable ZFS datasets that it + /// believes were associated with now-expunged Omicron zones. + pub destroy_orphans: bool, +} diff --git a/sled-agent/types/versions/src/initial/diagnostics.rs b/sled-agent/types/versions/src/initial/diagnostics.rs new file mode 100644 index 00000000000..79e5c8160c7 --- /dev/null +++ b/sled-agent/types/versions/src/initial/diagnostics.rs @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Diagnostics types for Sled Agent API v1. + +use schemars::JsonSchema; +use serde::Deserialize; + +/// Path parameters for sled-diagnostics log requests used by support bundles. +// NOTE: The original type had a typo (Parm vs Param). We keep both for compat. +#[derive(Deserialize, JsonSchema)] +pub struct SledDiagnosticsLogsDownloadPathParam { + /// The zone for which one would like to collect logs for + pub zone: String, +} + +/// Type alias for backward compatibility with the original typo. +pub type SledDiagnosticsLogsDownloadPathParm = + SledDiagnosticsLogsDownloadPathParam; + +/// Query parameters for sled-diagnostics log download requests. +#[derive(Deserialize, JsonSchema)] +pub struct SledDiagnosticsLogsDownloadQueryParam { + /// The max number of rotated logs to include in the final support bundle + pub max_rotated: usize, +} diff --git a/sled-agent/types/versions/src/initial/disk.rs b/sled-agent/types/versions/src/initial/disk.rs new file mode 100644 index 00000000000..5ba09e00f65 --- /dev/null +++ b/sled-agent/types/versions/src/initial/disk.rs @@ -0,0 +1,74 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Disk types for the Sled Agent API. + +use omicron_common::api::internal::nexus::DiskRuntimeState; +use omicron_common::disk::DiskVariant; +use omicron_uuid_kinds::ZpoolUuid; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// Path parameters for Disk requests. +#[derive(Deserialize, JsonSchema)] +pub struct DiskPathParam { + pub disk_id: Uuid, +} + +/// Information about a zpool. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +pub struct Zpool { + pub id: ZpoolUuid, + pub disk_type: DiskType, +} + +/// Disk type classification. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +pub enum DiskType { + U2, + M2, +} + +impl From for DiskType { + fn from(v: DiskVariant) -> Self { + match v { + DiskVariant::U2 => Self::U2, + DiskVariant::M2 => Self::M2, + } + } +} + +/// Sent from to a sled agent to establish the runtime state of a Disk +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct DiskEnsureBody { + /// Last runtime state of the Disk known to Nexus (used if the agent has + /// never seen this Disk before). + pub initial_runtime: DiskRuntimeState, + /// requested runtime state of the Disk + pub target: DiskStateRequested, +} + +/// Used to request a Disk state change +#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize, JsonSchema)] +#[serde(rename_all = "lowercase", tag = "state", content = "instance")] +pub enum DiskStateRequested { + Detached, + Attached(Uuid), + Destroyed, + Faulted, +} + +impl DiskStateRequested { + /// Returns whether the requested state is attached to an Instance or not. + pub fn is_attached(&self) -> bool { + match self { + DiskStateRequested::Detached => false, + DiskStateRequested::Destroyed => false, + DiskStateRequested::Faulted => false, + + DiskStateRequested::Attached(_) => true, + } + } +} diff --git a/sled-agent/types/versions/src/initial/early_networking.rs b/sled-agent/types/versions/src/initial/early_networking.rs new file mode 100644 index 00000000000..7c18ea21a46 --- /dev/null +++ b/sled-agent/types/versions/src/initial/early_networking.rs @@ -0,0 +1,622 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Types for network setup required to bring up the control plane. + +use std::str::FromStr; + +use bootstore::schemes::v0 as bootstore; +use omicron_common::api::internal::shared::RackNetworkConfig; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use slog::{Logger, warn}; + +/// Network configuration required to bring up the control plane +/// +/// The fields in this structure are those from +/// `RackInitializeRequest` necessary for use beyond RSS. +/// This is just for the initial rack configuration and cold boot purposes. +/// Updates come from Nexus. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +pub struct EarlyNetworkConfig { + // The current generation number of data as stored in CRDB. + // The initial generation is set during RSS time and then only mutated + // by Nexus. + pub generation: u64, + + // Which version of the data structure do we have. This is to help with + // deserialization and conversion in future updates. + pub schema_version: u32, + + // The actual configuration details + pub body: EarlyNetworkConfigBody, +} + +impl FromStr for EarlyNetworkConfig { + type Err = String; + + fn from_str(value: &str) -> Result { + #[derive(Deserialize)] + struct ShadowConfig { + generation: u64, + schema_version: u32, + body: EarlyNetworkConfigBody, + } + + let v2_err = match serde_json::from_str::(&value) { + Ok(cfg) => { + return Ok(EarlyNetworkConfig { + generation: cfg.generation, + schema_version: cfg.schema_version, + body: cfg.body, + }); + } + Err(e) => format!("unable to parse EarlyNetworkConfig: {e:?}"), + }; + // If we fail to parse the config as any known version, we return the + // error corresponding to the parse failure of the newest schema. + serde_json::from_str::(&value) + .map(|v1| EarlyNetworkConfig { + generation: v1.generation, + schema_version: Self::schema_version(), + body: v1.body.into(), + }) + .map_err(|_| v2_err) + } +} + +impl EarlyNetworkConfig { + pub fn schema_version() -> u32 { + 2 + } + + // Note: This currently only converts between v0 and v1 or deserializes v1 of + // `EarlyNetworkConfig`. + pub fn deserialize_bootstore_config( + log: &Logger, + config: &bootstore::NetworkConfig, + ) -> Result { + // Try to deserialize the latest version of the data structure (v2). If + // that succeeds we are done. + let v2_error = + match serde_json::from_slice::(&config.blob) { + Ok(val) => return Ok(val), + Err(error) => { + // Log this error and continue trying to deserialize older + // versions. + warn!( + log, + "Failed to deserialize EarlyNetworkConfig \ + as v2, trying next as v1: {}", + error, + ); + error + } + }; + + match serde_json::from_slice::( + &config.blob, + ) { + Ok(v1) => { + // Convert from v1 to v2 + return Ok(EarlyNetworkConfig { + generation: v1.generation, + schema_version: EarlyNetworkConfig::schema_version(), + body: v1.body.into(), + }); + } + Err(error) => { + // Log this error. + warn!( + log, + "Failed to deserialize EarlyNetworkConfig \ + as v1, trying next as v0: {}", + error + ); + } + }; + + match serde_json::from_slice::( + &config.blob, + ) { + Ok(val) => { + // Convert from v0 to v2 + return Ok(EarlyNetworkConfig { + generation: val.generation, + schema_version: 2, + body: EarlyNetworkConfigBody { + ntp_servers: val.ntp_servers, + rack_network_config: val.rack_network_config.map( + |v0_config| { + back_compat::RackNetworkConfigV0::to_v2( + val.rack_subnet, + v0_config, + ) + }, + ), + }, + }); + } + Err(error) => { + // Log this error. + warn!( + log, + "Failed to deserialize EarlyNetworkConfig as v0: {}", error, + ); + } + }; + + // If we fail to parse the config as any known version, we return the + // error corresponding to the parse failure of the newest schema. + Err(v2_error) + } +} + +/// This is the actual configuration of EarlyNetworking. +/// +/// We nest it below the "header" of `generation` and `schema_version` so that +/// we can perform partial deserialization of `EarlyNetworkConfig` to only read +/// the header and defer deserialization of the body once we know the schema +/// version. This is possible via the use of [`serde_json::value::RawValue`] in +/// future (post-v1) deserialization paths. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq)] +pub struct EarlyNetworkConfigBody { + /// The external NTP server addresses. + pub ntp_servers: Vec, + + // Rack network configuration as delivered from RSS or Nexus + pub rack_network_config: Option, +} + +impl From for bootstore::NetworkConfig { + fn from(value: EarlyNetworkConfig) -> Self { + // Can this ever actually fail? + // We literally just deserialized the same data in RSS + let blob = serde_json::to_vec(&value).unwrap(); + + // Yes this is duplicated, but that seems fine. + let generation = value.generation; + + bootstore::NetworkConfig { generation, blob } + } +} + +/// Structures and routines used to maintain backwards compatibility. The +/// contents of this module should only be used to convert older data into the +/// current format, and not for any ongoing run-time operations. +pub mod back_compat { + use std::net::{Ipv4Addr, Ipv6Addr}; + + use omicron_common::api::{ + external::SwitchLocation, + internal::shared::{ + BfdPeerConfig, BgpConfig, BgpPeerConfig, PortConfigV2, PortFec, + PortSpeed, RackNetworkConfigV2, RouteConfig, UplinkAddressConfig, + }, + }; + use oxnet::{IpNet, Ipv4Net, Ipv6Net}; + use schemars::JsonSchema; + use serde::{Deserialize, Serialize}; + + use super::EarlyNetworkConfigBody; + + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + pub struct EarlyNetworkConfigBodyV1 { + /// The external NTP server addresses. + pub ntp_servers: Vec, + + // Rack network configuration as delivered from RSS or Nexus + pub rack_network_config: Option, + } + + impl From for EarlyNetworkConfigBody { + fn from(v1: EarlyNetworkConfigBodyV1) -> Self { + EarlyNetworkConfigBody { + ntp_servers: v1.ntp_servers, + rack_network_config: v1 + .rack_network_config + .map(|v1_config| v1_config.into()), + } + } + } + + /// Deprecated, use `RackNetworkConfig` instead. Cannot actually deprecate due to + /// + /// + /// Our first version of `RackNetworkConfig`. If this exists in the bootstore, we + /// upgrade out of it into `RackNetworkConfigV1` or later versions if possible. + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] + pub struct RackNetworkConfigV0 { + // TODO: #3591 Consider making infra-ip ranges implicit for uplinks + /// First ip address to be used for configuring network infrastructure + pub infra_ip_first: Ipv4Addr, + /// Last ip address to be used for configuring network infrastructure + pub infra_ip_last: Ipv4Addr, + /// Uplinks for connecting the rack to external networks + pub uplinks: Vec, + } + + impl RackNetworkConfigV0 { + /// Convert from `RackNetworkConfigV0` to `RackNetworkConfigV1` + /// + /// We cannot use `From for `RackNetworkConfigV2` + /// because the `rack_subnet` field does not exist in `RackNetworkConfigV0` + /// and must be passed in from the `EarlyNetworkConfigV0` struct which + /// contains the `RackNetworkConfigV0` struct. + pub fn to_v2( + rack_subnet: Ipv6Addr, + v0: RackNetworkConfigV0, + ) -> RackNetworkConfigV2 { + RackNetworkConfigV2 { + rack_subnet: Ipv6Net::new(rack_subnet, 56).unwrap(), + infra_ip_first: v0.infra_ip_first, + infra_ip_last: v0.infra_ip_last, + ports: v0 + .uplinks + .into_iter() + .map(|uplink| PortConfigV2::from(uplink)) + .collect(), + bgp: vec![], + bfd: vec![], + } + } + } + + /// Deprecated, use PortConfigV2 instead. Cannot actually deprecate due to + /// + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] + pub struct PortConfigV1 { + /// The set of routes associated with this port. + pub routes: Vec, + /// This port's addresses and optional vlan IDs + pub addresses: Vec, + /// Switch the port belongs to. + pub switch: SwitchLocation, + /// Nmae of the port this config applies to. + pub port: String, + /// Port speed. + pub uplink_port_speed: PortSpeed, + /// Port forward error correction type. + pub uplink_port_fec: PortFec, + /// BGP peers on this port + pub bgp_peers: Vec, + /// Whether or not to set autonegotiation + #[serde(default)] + pub autoneg: bool, + } + + impl From for PortConfigV2 { + fn from(v1: PortConfigV1) -> Self { + PortConfigV2 { + routes: v1.routes.clone(), + addresses: v1 + .addresses + .iter() + .map(|a| UplinkAddressConfig { address: *a, vlan_id: None }) + .collect(), + switch: v1.switch, + port: v1.port, + uplink_port_speed: v1.uplink_port_speed, + uplink_port_fec: Some(v1.uplink_port_fec), + bgp_peers: v1.bgp_peers.clone(), + autoneg: v1.autoneg, + lldp: None, + tx_eq: None, + } + } + } + + /// Deprecated, use PortConfigV2 instead. Cannot actually deprecate due to + /// + #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, JsonSchema)] + pub struct UplinkConfig { + /// Gateway address + pub gateway_ip: Ipv4Addr, + /// Switch to use for uplink + pub switch: SwitchLocation, + /// Switchport to use for external connectivity + pub uplink_port: String, + /// Speed for the Switchport + pub uplink_port_speed: PortSpeed, + /// Forward Error Correction setting for the uplink port + pub uplink_port_fec: PortFec, + /// IP Address and prefix (e.g., `192.168.0.1/16`) to apply to switchport + /// (must be in infra_ip pool) + pub uplink_cidr: Ipv4Net, + /// VLAN id to use for uplink + pub uplink_vid: Option, + /// RIB Priority + pub rib_priority: Option, + } + + impl From for PortConfigV2 { + fn from(value: UplinkConfig) -> Self { + PortConfigV2 { + routes: vec![RouteConfig { + destination: "0.0.0.0/0".parse().unwrap(), + nexthop: value.gateway_ip.into(), + vlan_id: value.uplink_vid, + rib_priority: value.rib_priority, + }], + addresses: vec![UplinkAddressConfig { + address: value.uplink_cidr.into(), + vlan_id: value.uplink_vid, + }], + switch: value.switch, + port: value.uplink_port, + uplink_port_speed: value.uplink_port_speed, + uplink_port_fec: Some(value.uplink_port_fec), + bgp_peers: vec![], + autoneg: false, + lldp: None, + tx_eq: None, + } + } + } + + /// Deprecated, use `RackNetworkConfig` instead. Cannot actually deprecate due to + /// + /// + /// Our second version of `RackNetworkConfig`. If this exists in the bootstore, + /// we upgrade out of it into `RackNetworkConfigV1` or later versions if + /// possible. + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] + pub struct RackNetworkConfigV1 { + pub rack_subnet: Ipv6Net, + // TODO: #3591 Consider making infra-ip ranges implicit for uplinks + /// First ip address to be used for configuring network infrastructure + pub infra_ip_first: Ipv4Addr, + /// Last ip address to be used for configuring network infrastructure + pub infra_ip_last: Ipv4Addr, + /// Uplinks for connecting the rack to external networks + pub ports: Vec, + /// BGP configurations for connecting the rack to external networks + pub bgp: Vec, + /// BFD configuration for connecting the rack to external networks + #[serde(default)] + pub bfd: Vec, + } + + impl From for RackNetworkConfigV2 { + fn from(v1: RackNetworkConfigV1) -> Self { + RackNetworkConfigV2 { + rack_subnet: v1.rack_subnet, + infra_ip_first: v1.infra_ip_first, + infra_ip_last: v1.infra_ip_last, + ports: v1 + .ports + .into_iter() + .map(|ports| PortConfigV2::from(ports)) + .collect(), + bgp: v1.bgp.clone(), + bfd: v1.bfd.clone(), + } + } + } + + // The second production version of the `EarlyNetworkConfig`. + // + // If this version is in the bootstore than we need to convert it to + // `EarlyNetworkConfigV2`. + // + // Once we do this for all customers that have initialized racks with the + // old version we can go ahead and remove this type and its conversion code + // altogether. + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + pub struct EarlyNetworkConfigV1 { + // The current generation number of data as stored in CRDB. + // The initial generation is set during RSS time and then only mutated + // by Nexus. + pub generation: u64, + + // Which version of the data structure do we have. This is to help with + // deserialization and conversion in future updates. + pub schema_version: u32, + + // The actual configuration details + pub body: EarlyNetworkConfigBodyV1, + } + + // The first production version of the `EarlyNetworkConfig`. + // + // If this version is in the bootstore than we need to convert it to + // `EarlyNetworkConfigV2`. + // + // Once we do this for all customers that have initialized racks with the + // old version we can go ahead and remove this type and its conversion code + // altogether. + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] + pub struct EarlyNetworkConfigV0 { + // The current generation number of data as stored in CRDB. + // The initial generation is set during RSS time and then only mutated + // by Nexus. + pub generation: u64, + + pub rack_subnet: Ipv6Addr, + + /// The external NTP server addresses. + pub ntp_servers: Vec, + + // Rack network configuration as delivered from RSS and only existing at + // generation 1 + pub rack_network_config: Option, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::net::Ipv4Addr; + use std::net::Ipv6Addr; + + use omicron_common::api::external::SwitchLocation; + use omicron_common::api::internal::shared::PortConfigV2; + use omicron_common::api::internal::shared::PortFec; + use omicron_common::api::internal::shared::PortSpeed; + use omicron_common::api::internal::shared::RackNetworkConfigV2; + use omicron_common::api::internal::shared::RouteConfig; + use omicron_common::api::internal::shared::UplinkAddressConfig; + use omicron_test_utils::dev::test_setup_log; + use oxnet::Ipv6Net; + + #[test] + fn serialized_early_network_config_v0_to_v2_conversion() { + let logctx = test_setup_log( + "serialized_early_network_config_v0_to_v2_conversion", + ); + let v0 = back_compat::EarlyNetworkConfigV0 { + generation: 1, + rack_subnet: Ipv6Addr::UNSPECIFIED, + ntp_servers: Vec::new(), + rack_network_config: Some(back_compat::RackNetworkConfigV0 { + infra_ip_first: Ipv4Addr::UNSPECIFIED, + infra_ip_last: Ipv4Addr::UNSPECIFIED, + uplinks: vec![back_compat::UplinkConfig { + gateway_ip: Ipv4Addr::UNSPECIFIED, + switch: SwitchLocation::Switch0, + uplink_port: "Port0".to_string(), + uplink_port_speed: PortSpeed::Speed100G, + uplink_port_fec: PortFec::None, + uplink_cidr: "192.168.0.1/16".parse().unwrap(), + uplink_vid: None, + rib_priority: None, + }], + }), + }; + + let v0_serialized = serde_json::to_vec(&v0).unwrap(); + let bootstore_conf = + bootstore::NetworkConfig { generation: 1, blob: v0_serialized }; + + let v2 = EarlyNetworkConfig::deserialize_bootstore_config( + &logctx.log, + &bootstore_conf, + ) + .unwrap(); + let v0_rack_network_config = v0.rack_network_config.unwrap(); + let uplink = v0_rack_network_config.uplinks[0].clone(); + let expected = EarlyNetworkConfig { + generation: 1, + schema_version: EarlyNetworkConfig::schema_version(), + body: EarlyNetworkConfigBody { + ntp_servers: v0.ntp_servers.clone(), + rack_network_config: Some(RackNetworkConfigV2 { + rack_subnet: Ipv6Net::new(v0.rack_subnet, 56).unwrap(), + infra_ip_first: v0_rack_network_config.infra_ip_first, + infra_ip_last: v0_rack_network_config.infra_ip_last, + ports: vec![PortConfigV2 { + routes: vec![RouteConfig { + destination: "0.0.0.0/0".parse().unwrap(), + nexthop: uplink.gateway_ip.into(), + vlan_id: None, + rib_priority: None, + }], + addresses: vec![UplinkAddressConfig { + address: uplink.uplink_cidr.into(), + vlan_id: None, + }], + switch: uplink.switch, + port: uplink.uplink_port, + uplink_port_speed: uplink.uplink_port_speed, + uplink_port_fec: Some(uplink.uplink_port_fec), + autoneg: false, + bgp_peers: vec![], + lldp: None, + tx_eq: None, + }], + bgp: vec![], + bfd: vec![], + }), + }, + }; + + assert_eq!(expected, v2); + + logctx.cleanup_successful(); + } + + #[test] + fn serialized_early_network_config_v1_to_v2_conversion() { + let logctx = test_setup_log( + "serialized_early_network_config_v1_to_v2_conversion", + ); + + let v1 = back_compat::EarlyNetworkConfigV1 { + generation: 1, + schema_version: 1, + body: back_compat::EarlyNetworkConfigBodyV1 { + ntp_servers: Vec::new(), + rack_network_config: Some(back_compat::RackNetworkConfigV1 { + rack_subnet: Ipv6Net::new(Ipv6Addr::UNSPECIFIED, 56) + .unwrap(), + infra_ip_first: Ipv4Addr::UNSPECIFIED, + infra_ip_last: Ipv4Addr::UNSPECIFIED, + ports: vec![back_compat::PortConfigV1 { + routes: vec![RouteConfig { + destination: "0.0.0.0/0".parse().unwrap(), + nexthop: "192.168.0.2".parse().unwrap(), + vlan_id: None, + rib_priority: None, + }], + addresses: vec!["192.168.0.1/16".parse().unwrap()], + switch: SwitchLocation::Switch0, + port: "Port0".to_string(), + uplink_port_speed: PortSpeed::Speed100G, + uplink_port_fec: PortFec::None, + bgp_peers: Vec::new(), + autoneg: false, + }], + bgp: Vec::new(), + bfd: Vec::new(), + }), + }, + }; + + let v1_serialized = serde_json::to_vec(&v1).unwrap(); + let bootstore_conf = + bootstore::NetworkConfig { generation: 1, blob: v1_serialized }; + + let v2 = EarlyNetworkConfig::deserialize_bootstore_config( + &logctx.log, + &bootstore_conf, + ) + .unwrap(); + let v1_rack_network_config = v1.body.rack_network_config.unwrap(); + let port = v1_rack_network_config.ports[0].clone(); + let expected = EarlyNetworkConfig { + generation: 1, + schema_version: EarlyNetworkConfig::schema_version(), + body: EarlyNetworkConfigBody { + ntp_servers: v1.body.ntp_servers.clone(), + rack_network_config: Some(RackNetworkConfigV2 { + rack_subnet: v1_rack_network_config.rack_subnet, + infra_ip_first: v1_rack_network_config.infra_ip_first, + infra_ip_last: v1_rack_network_config.infra_ip_last, + ports: vec![PortConfigV2 { + routes: port.routes.clone(), + addresses: vec![UplinkAddressConfig { + address: port.addresses[0], + vlan_id: None, + }], + switch: port.switch, + port: port.port, + uplink_port_speed: port.uplink_port_speed, + uplink_port_fec: Some(port.uplink_port_fec), + autoneg: false, + bgp_peers: vec![], + lldp: None, + tx_eq: None, + }], + bgp: vec![], + bfd: vec![], + }), + }, + }; + + assert_eq!(expected, v2); + + logctx.cleanup_successful(); + } +} diff --git a/sled-agent/types/versions/src/initial/instance.rs b/sled-agent/types/versions/src/initial/instance.rs new file mode 100644 index 00000000000..3c05b49134f --- /dev/null +++ b/sled-agent/types/versions/src/initial/instance.rs @@ -0,0 +1,270 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Instance types for Sled Agent API versions 1-6. + +use std::collections::HashSet; +use std::net::{IpAddr, SocketAddr}; + +use omicron_common::api::external; +use omicron_common::api::external::Hostname; +use omicron_common::api::internal::nexus::{HostIdentifier, VmmRuntimeState}; +use omicron_common::api::internal::shared::DhcpConfig; +use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; +use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; +use omicron_uuid_kinds::{InstanceUuid, PropolisUuid}; +use propolis_api_types::instance_spec::{ + SpecKey, + components::backends::{ + CrucibleStorageBackend, FileStorageBackend, VirtioNetworkBackend, + }, + v0::{ComponentV0, InstanceSpecV0}, +}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// Path parameters for VMM requests. +#[derive(Deserialize, JsonSchema)] +pub struct VmmPathParam { + pub propolis_id: PropolisUuid, +} + +/// Path parameters for VMM disk snapshot requests. +#[derive(Deserialize, JsonSchema)] +pub struct VmmIssueDiskSnapshotRequestPathParam { + pub propolis_id: PropolisUuid, + pub disk_id: Uuid, +} + +/// Request body for VMM disk snapshot requests. +#[derive(Deserialize, JsonSchema)] +pub struct VmmIssueDiskSnapshotRequestBody { + pub snapshot_id: Uuid, +} + +/// Response for VMM disk snapshot requests. +#[derive(Serialize, JsonSchema)] +pub struct VmmIssueDiskSnapshotRequestResponse { + pub snapshot_id: Uuid, +} + +/// Path parameters for VPC requests. +#[derive(Deserialize, JsonSchema)] +pub struct VpcPathParam { + pub vpc_id: Uuid, +} + +/// The body of a request to ensure that a instance and VMM are known to a sled +/// agent. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct InstanceEnsureBody { + /// The virtual hardware configuration this virtual machine should have when + /// it is started. + pub vmm_spec: VmmSpec, + + /// Information about the sled-local configuration that needs to be + /// established to make the VM's virtual hardware fully functional. + pub local_config: InstanceSledLocalConfig, + + /// The initial VMM runtime state for the VMM being registered. + pub vmm_runtime: VmmRuntimeState, + + /// The ID of the instance for which this VMM is being created. + pub instance_id: InstanceUuid, + + /// The ID of the migration in to this VMM, if this VMM is being + /// ensured is part of a migration in. If this is `None`, the VMM is not + /// being created due to a migration. + pub migration_id: Option, + + /// The address at which this VMM should serve a Propolis server API. + pub propolis_addr: SocketAddr, + + /// Metadata used to track instance statistics. + pub metadata: InstanceMetadata, +} + +/// Describes sled-local configuration that a sled-agent must establish to make +/// the instance's virtual hardware fully functional. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct InstanceSledLocalConfig { + pub hostname: Hostname, + pub nics: Vec, + pub source_nat: SourceNatConfig, + /// Zero or more external IP addresses (either floating or ephemeral), + /// provided to an instance to allow inbound connectivity. + pub ephemeral_ip: Option, + pub floating_ips: Vec, + pub firewall_rules: Vec, + pub dhcp_config: DhcpConfig, +} + +/// Metadata used to track statistics about an instance. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct InstanceMetadata { + pub silo_id: Uuid, + pub project_id: Uuid, +} + +/// Specifies the virtual hardware configuration of a new Propolis VMM in the +/// form of a Propolis instance specification. +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct VmmSpec(pub InstanceSpecV0); + +/// Extension trait for VmmSpec to provide helper methods. +pub trait VmmSpecExt { + fn crucible_backends( + &self, + ) -> impl Iterator; + + fn viona_backends( + &self, + ) -> impl Iterator; + + fn file_backends( + &self, + ) -> impl Iterator; +} + +impl VmmSpecExt for VmmSpec { + fn crucible_backends( + &self, + ) -> impl Iterator { + self.0.components.iter().filter_map( + |(key, component)| match component { + ComponentV0::CrucibleStorageBackend(be) => Some((key, be)), + _ => None, + }, + ) + } + + fn viona_backends( + &self, + ) -> impl Iterator { + self.0.components.iter().filter_map( + |(key, component)| match component { + ComponentV0::VirtioNetworkBackend(be) => Some((key, be)), + _ => None, + }, + ) + } + + fn file_backends( + &self, + ) -> impl Iterator { + self.0.components.iter().filter_map( + |(key, component)| match component { + ComponentV0::FileStorageBackend(be) => Some((key, be)), + _ => None, + }, + ) + } +} + +/// VPC firewall rule after object name resolution has been performed by Nexus +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +pub struct ResolvedVpcFirewallRule { + pub status: external::VpcFirewallRuleStatus, + pub direction: external::VpcFirewallRuleDirection, + pub targets: Vec, + pub filter_hosts: Option>, + pub filter_ports: Option>, + pub filter_protocols: Option>, + pub action: external::VpcFirewallRuleAction, + pub priority: external::VpcFirewallRulePriority, +} + +/// The body of a request to move a previously-ensured instance into a specific +/// runtime state. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct VmmPutStateBody { + /// The state into which the instance should be driven. + pub state: VmmStateRequested, +} + +/// The response sent from a request to move an instance into a specific runtime +/// state. +#[derive(Debug, Serialize, Deserialize, JsonSchema)] +pub struct VmmPutStateResponse { + /// The current runtime state of the instance after handling the request to + /// change its state. If the instance's state did not change, this field is + /// `None`. + pub updated_runtime: + Option, +} + +/// Requestable running state of an Instance. +/// +/// A subset of [`omicron_common::api::external::InstanceState`]. +#[derive(Copy, Clone, Debug, Deserialize, Serialize, JsonSchema)] +#[serde(rename_all = "snake_case", tag = "type", content = "value")] +pub enum VmmStateRequested { + /// Run this instance by migrating in from a previous running incarnation of + /// the instance. + MigrationTarget(InstanceMigrationTargetParams), + /// Start the instance if it is not already running. + Running, + /// Stop the instance. + Stopped, + /// Immediately reset the instance, as though it had stopped and immediately + /// began to run again. + Reboot, +} + +impl std::fmt::Display for VmmStateRequested { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.label()) + } +} + +impl VmmStateRequested { + fn label(&self) -> &str { + match self { + VmmStateRequested::MigrationTarget(_) => "migrating in", + VmmStateRequested::Running => "running", + VmmStateRequested::Stopped => "stopped", + VmmStateRequested::Reboot => "reboot", + } + } + + /// Returns true if the state represents a stopped Instance. + pub fn is_stopped(&self) -> bool { + match self { + VmmStateRequested::MigrationTarget(_) => false, + VmmStateRequested::Running => false, + VmmStateRequested::Stopped => true, + VmmStateRequested::Reboot => false, + } + } +} + +/// The response sent from a request to unregister an instance. +#[derive(Serialize, Deserialize, JsonSchema)] +pub struct VmmUnregisterResponse { + /// The current state of the instance after handling the request to + /// unregister it. If the instance's state did not change, this field is + /// `None`. + pub updated_runtime: + Option, +} + +/// Parameters used when directing Propolis to initialize itself via live +/// migration. +#[derive(Copy, Clone, Debug, Deserialize, Serialize, JsonSchema)] +pub struct InstanceMigrationTargetParams { + /// The address of the Propolis server that will serve as the migration + /// source. + pub src_propolis_addr: SocketAddr, +} + +/// Used to dynamically update external IPs attached to an instance. +#[derive( + Copy, Clone, Debug, Eq, PartialEq, Hash, Deserialize, JsonSchema, Serialize, +)] +#[serde(rename_all = "snake_case", tag = "type", content = "value")] +pub enum InstanceExternalIpBody { + Ephemeral(IpAddr), + Floating(IpAddr), +} diff --git a/nexus-sled-agent-shared/src/inventory.rs b/sled-agent/types/versions/src/initial/inventory.rs similarity index 74% rename from nexus-sled-agent-shared/src/inventory.rs rename to sled-agent/types/versions/src/initial/inventory.rs index c154ba4eb80..282425e59e9 100644 --- a/nexus-sled-agent-shared/src/inventory.rs +++ b/sled-agent/types/versions/src/initial/inventory.rs @@ -2,7 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Inventory types shared between Nexus and sled-agent. +//! Inventory types for Sled Agent API versions 1-3. use std::collections::BTreeMap; use std::fmt::{self, Write}; @@ -16,33 +16,29 @@ use iddqd::IdOrdItem; use iddqd::IdOrdMap; use iddqd::id_upcast; use indent_write::fmt::IndentWriter; -use omicron_common::disk::{DatasetKind, DatasetName, M2Slot}; -use omicron_common::ledger::Ledgerable; +use omicron_common::api::external::{ByteCount, Generation}; +use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; +use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; +use omicron_common::disk::{ + DatasetConfig, DatasetName, DiskVariant, M2Slot, OmicronPhysicalDiskConfig, +}; use omicron_common::snake_case_result; use omicron_common::snake_case_result::SnakeCaseResult; -use omicron_common::update::OmicronZoneManifestSource; -use omicron_common::{ - api::{ - external::{ByteCount, Generation}, - internal::shared::{NetworkInterface, SourceNatConfigGeneric}, - }, - disk::{DatasetConfig, DiskVariant, OmicronPhysicalDiskConfig}, - update::ArtifactId, - zpool_name::ZpoolName, -}; +use omicron_common::update::{ArtifactId, OmicronZoneManifestSource}; +use omicron_common::zpool_name::ZpoolName; use omicron_uuid_kinds::{ - DatasetUuid, InternalZpoolUuid, MupdateUuid, OmicronZoneUuid, + DatasetUuid, InternalZpoolUuid, MupdateOverrideUuid, MupdateUuid, + OmicronZoneUuid, PhysicalDiskUuid, SledUuid, ZpoolUuid, }; -use omicron_uuid_kinds::{MupdateOverrideUuid, PhysicalDiskUuid}; -use omicron_uuid_kinds::{SledUuid, ZpoolUuid}; use schemars::schema::{Schema, SchemaObject}; -use schemars::{JsonSchema, SchemaGenerator}; +use schemars::{JsonSchema, r#gen::SchemaGenerator}; use serde::{Deserialize, Serialize}; +use tufaceous_artifact::KnownArtifactKind; // Export these types for convenience -- this way, dependents don't have to // depend on sled-hardware-types. pub use sled_hardware_types::{Baseboard, SledCpuFamily}; use strum::EnumIter; -use tufaceous_artifact::{ArtifactHash, KnownArtifactKind}; +use tufaceous_artifact::ArtifactHash; /// Identifies information about disks which may be attached to Sleds. #[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] @@ -110,143 +106,89 @@ impl From for InventoryDataset { } } -/// Identity and basic status information about this sled agent -#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] -pub struct Inventory { - pub sled_id: SledUuid, - pub sled_agent_address: SocketAddrV6, - pub sled_role: SledRole, - pub baseboard: Baseboard, - pub usable_hardware_threads: u32, - pub usable_physical_ram: ByteCount, - pub cpu_family: SledCpuFamily, - pub reservoir_size: ByteCount, - pub disks: Vec, - pub zpools: Vec, - pub datasets: Vec, - pub ledgered_sled_config: Option, - pub reconciler_status: ConfigReconcilerInventoryStatus, - pub last_reconciliation: Option, - pub zone_image_resolver: ZoneImageResolverInventory, +/// Describes the role of the sled within the rack. +/// +/// Note that this may change if the sled is physically moved +/// within the rack. +#[derive( + Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, +)] +#[serde(rename_all = "snake_case")] +pub enum SledRole { + /// The sled is a general compute sled. + Gimlet, + /// The sled is attached to the network switch, and has additional + /// responsibilities. + Scrimlet, } -/// Describes the last attempt made by the sled-agent-config-reconciler to -/// reconcile the current sled config against the actual state of the sled. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] -#[serde(rename_all = "snake_case")] -pub struct ConfigReconcilerInventory { - pub last_reconciled_config: OmicronSledConfig, - pub external_disks: - BTreeMap, - pub datasets: BTreeMap, - pub orphaned_datasets: IdOrdMap, - pub zones: BTreeMap, - pub boot_partitions: BootPartitionContents, - /// The result of removing the mupdate override file on disk. +/// Describes the desired contents of a host phase 2 slot (i.e., the boot +/// partition on one of the internal M.2 drives). +#[derive( + Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, +)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum HostPhase2DesiredContents { + /// Do not change the current contents. /// - /// `None` if `remove_mupdate_override` was not provided in the sled config. - pub remove_mupdate_override: Option, -} + /// We use this value when we've detected a sled has been mupdated (and we + /// don't want to overwrite phase 2 images until we understand how to + /// recover from that mupdate) and as the default value when reading an + /// [`OmicronSledConfig`] that was ledgered before this concept existed. + CurrentContents, -impl ConfigReconcilerInventory { - /// Iterate over all running zones as reported by the last reconciliation - /// result. + /// Set the phase 2 slot to the given artifact. /// - /// This includes zones that are both present in `last_reconciled_config` - /// and whose status in `zones` indicates "successfully running". - pub fn running_omicron_zones( - &self, - ) -> impl Iterator { - self.zones.iter().filter_map(|(zone_id, result)| match result { - ConfigReconcilerInventoryResult::Ok => { - self.last_reconciled_config.zones.get(zone_id) - } - ConfigReconcilerInventoryResult::Err { .. } => None, - }) - } + /// The artifact will come from an unpacked and distributed TUF repo. + Artifact { hash: ArtifactHash }, +} - /// Iterate over all zones contained in the most-recently-reconciled sled - /// config and report their status as of that reconciliation. - pub fn reconciled_omicron_zones( - &self, - ) -> impl Iterator - { - // `self.zones` may contain zone IDs that aren't present in - // `last_reconciled_config` at all, if we failed to _shut down_ zones - // that are no longer present in the config. We use `filter_map` to - // strip those out, and only report on the configured zones. - self.zones.iter().filter_map(|(zone_id, result)| { - let config = self.last_reconciled_config.zones.get(zone_id)?; - Some((config, result)) - }) +impl HostPhase2DesiredContents { + /// The artifact hash described by `self`, if it has one. + pub fn artifact_hash(&self) -> Option { + match self { + Self::CurrentContents => None, + Self::Artifact { hash } => Some(*hash), + } } +} - /// Given a sled config, produce a reconciler result that sled-agent could - /// have emitted if reconciliation succeeded. - /// - /// This method should only be used by tests and dev tools; real code should - /// look at the actual `last_reconciliation` value from the parent - /// [`Inventory`]. - pub fn debug_assume_success(config: OmicronSledConfig) -> Self { - let mut ret = Self { - // These fields will be filled in by `debug_update_assume_success`. - last_reconciled_config: OmicronSledConfig::default(), - external_disks: BTreeMap::new(), - datasets: BTreeMap::new(), - orphaned_datasets: IdOrdMap::new(), - zones: BTreeMap::new(), - remove_mupdate_override: None, - - // These fields will not. - boot_partitions: BootPartitionContents::debug_assume_success(), - }; - - ret.debug_update_assume_success(config); +/// Describes the desired contents for both host phase 2 slots. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct HostPhase2DesiredSlots { + pub slot_a: HostPhase2DesiredContents, + pub slot_b: HostPhase2DesiredContents, +} - ret +impl HostPhase2DesiredSlots { + /// Return a `HostPhase2DesiredSlots` with both slots set to + /// [`HostPhase2DesiredContents::CurrentContents`]; i.e., "make no changes + /// to the current contents of either slot". + pub const fn current_contents() -> Self { + Self { + slot_a: HostPhase2DesiredContents::CurrentContents, + slot_b: HostPhase2DesiredContents::CurrentContents, + } } +} - /// Given a sled config, update an existing reconciler result to simulate an - /// output that sled-agent could have emitted if reconciliation succeeded. - /// - /// This method should only be used by tests and dev tools; real code should - /// look at the actual `last_reconciliation` value from the parent - /// [`Inventory`]. - pub fn debug_update_assume_success(&mut self, config: OmicronSledConfig) { - let external_disks = config - .disks - .iter() - .map(|d| (d.id, ConfigReconcilerInventoryResult::Ok)) - .collect(); - let datasets = config - .datasets - .iter() - .map(|d| (d.id, ConfigReconcilerInventoryResult::Ok)) - .collect(); - let zones = config - .zones - .iter() - .map(|z| (z.id, ConfigReconcilerInventoryResult::Ok)) - .collect(); - let remove_mupdate_override = - config.remove_mupdate_override.map(|_| { - RemoveMupdateOverrideInventory { - boot_disk_result: Ok( - RemoveMupdateOverrideBootSuccessInventory::Removed, - ), - non_boot_message: "mupdate override successfully removed \ - on non-boot disks" - .to_owned(), - } - }); - - self.last_reconciled_config = config; - self.external_disks = external_disks; - self.datasets = datasets; - self.orphaned_datasets = IdOrdMap::new(); - self.zones = zones; - self.remove_mupdate_override = remove_mupdate_override; - } +/// Describes a persistent ZFS dataset associated with an Omicron zone +#[derive( + Clone, + Debug, + Deserialize, + Serialize, + JsonSchema, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + Diffable, +)] +pub struct OmicronZoneDataset { + pub pool_name: ZpoolName, } #[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] @@ -395,28 +337,6 @@ impl From> for ConfigReconcilerInventoryResult { } } -/// Status of the sled-agent-config-reconciler task. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] -#[serde(tag = "status", rename_all = "snake_case")] -pub enum ConfigReconcilerInventoryStatus { - /// The reconciler task has not yet run for the first time since sled-agent - /// started. - NotYetRun, - /// The reconciler task is actively running. - Running { - config: Box, - started_at: DateTime, - running_for: Duration, - }, - /// The reconciler task is currently idle, but previously did complete a - /// reconciliation attempt. - /// - /// This variant does not include the `OmicronSledConfig` used in the last - /// attempt, because that's always available via - /// [`ConfigReconcilerInventory::last_reconciled_config`]. - Idle { completed_at: DateTime, ran_for: Duration }, -} - /// Inventory representation of zone image resolver status and health. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] pub struct ZoneImageResolverInventory { @@ -957,526 +877,60 @@ impl fmt::Display for MupdateOverrideNonBootInventoryDisplay<'_> { } } -/// Describes the role of the sled within the rack. -/// -/// Note that this may change if the sled is physically moved -/// within the rack. -#[derive( - Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, -)] -#[serde(rename_all = "snake_case")] -pub enum SledRole { - /// The sled is a general compute sled. - Gimlet, - /// The sled is attached to the network switch, and has additional - /// responsibilities. - Scrimlet, -} - -/// Describes the desired contents of a host phase 2 slot (i.e., the boot -/// partition on one of the internal M.2 drives). +/// Where Sled Agent should get the image for a zone. #[derive( - Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, + Clone, + Debug, + Deserialize, + Serialize, + JsonSchema, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + Diffable, )] #[serde(tag = "type", rename_all = "snake_case")] -pub enum HostPhase2DesiredContents { - /// Do not change the current contents. +pub enum OmicronZoneImageSource { + /// This zone's image source is whatever happens to be on the sled's + /// "install" dataset. /// - /// We use this value when we've detected a sled has been mupdated (and we - /// don't want to overwrite phase 2 images until we understand how to - /// recover from that mupdate) and as the default value when reading an - /// [`OmicronSledConfig`] that was ledgered before this concept existed. - CurrentContents, - - /// Set the phase 2 slot to the given artifact. + /// This is whatever was put in place at the factory or by the latest + /// MUPdate. The image used here can vary by sled and even over time (if the + /// sled gets MUPdated again). /// - /// The artifact will come from an unpacked and distributed TUF repo. + /// Historically, this was the only source for zone images. In an system + /// with automated control-plane-driven update we expect to only use this + /// variant in emergencies where the system had to be recovered via MUPdate. + InstallDataset, + /// This zone's image source is the artifact matching this hash from the TUF + /// artifact store (aka "TUF repo depot"). + /// + /// This originates from TUF repos uploaded to Nexus which are then + /// replicated out to all sleds. Artifact { hash: ArtifactHash }, } -impl HostPhase2DesiredContents { - /// The artifact hash described by `self`, if it has one. +impl OmicronZoneImageSource { + /// Return the artifact hash used for the zone image, if the zone's image + /// source is from the artifact store. pub fn artifact_hash(&self) -> Option { - match self { - Self::CurrentContents => None, - Self::Artifact { hash } => Some(*hash), - } - } -} - -/// Describes the desired contents for both host phase 2 slots. -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] -#[serde(rename_all = "snake_case")] -pub struct HostPhase2DesiredSlots { - pub slot_a: HostPhase2DesiredContents, - pub slot_b: HostPhase2DesiredContents, -} - -impl HostPhase2DesiredSlots { - /// Return a `HostPhase2DesiredSlots` with both slots set to - /// [`HostPhase2DesiredContents::CurrentContents`]; i.e., "make no changes - /// to the current contents of either slot". - pub const fn current_contents() -> Self { - Self { - slot_a: HostPhase2DesiredContents::CurrentContents, - slot_b: HostPhase2DesiredContents::CurrentContents, + if let OmicronZoneImageSource::Artifact { hash } = self { + Some(*hash) + } else { + None } } -} - -/// Describes the set of Reconfigurator-managed configuration elements of a sled -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] -pub struct OmicronSledConfig { - pub generation: Generation, - // Serialize and deserialize disks, datasets, and zones as maps for - // backwards compatibility. Newer IdOrdMaps should not use IdOrdMapAsMap. - #[serde( - with = "iddqd::id_ord_map::IdOrdMapAsMap::" - )] - pub disks: IdOrdMap, - #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] - pub datasets: IdOrdMap, - #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] - pub zones: IdOrdMap, - pub remove_mupdate_override: Option, - #[serde(default = "HostPhase2DesiredSlots::current_contents")] - pub host_phase_2: HostPhase2DesiredSlots, -} -impl Default for OmicronSledConfig { - fn default() -> Self { - Self { - generation: Generation::new(), - disks: IdOrdMap::default(), - datasets: IdOrdMap::default(), - zones: IdOrdMap::default(), - remove_mupdate_override: None, - host_phase_2: HostPhase2DesiredSlots::current_contents(), - } + // See `OmicronZoneConfig`. This is a separate function instead of being + // `impl Default` because we don't want to accidentally use this default + // outside of `serde(default)`. + pub fn deserialize_default() -> Self { + OmicronZoneImageSource::InstallDataset } } -impl Ledgerable for OmicronSledConfig { - fn is_newer_than(&self, other: &Self) -> bool { - self.generation > other.generation - } - - fn generation_bump(&mut self) { - // DO NOTHING! - // - // Generation bumps must only ever come from nexus and will be encoded - // in the struct itself - } -} - -/// Describes the set of Omicron-managed zones running on a sled -#[derive( - Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, -)] -pub struct OmicronZonesConfig { - /// generation number of this configuration - /// - /// This generation number is owned by the control plane (i.e., RSS or - /// Nexus, depending on whether RSS-to-Nexus handoff has happened). It - /// should not be bumped within Sled Agent. - /// - /// Sled Agent rejects attempts to set the configuration to a generation - /// older than the one it's currently running. - pub generation: Generation, - - /// list of running zones - pub zones: Vec, -} - -impl OmicronZonesConfig { - /// Generation 1 of `OmicronZonesConfig` is always the set of no zones. - pub const INITIAL_GENERATION: Generation = Generation::from_u32(1); -} - -/// Describes one Omicron-managed zone running on a sled -#[derive( - Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, -)] -pub struct OmicronZoneConfig { - pub id: OmicronZoneUuid, - - /// The pool on which we'll place this zone's root filesystem. - /// - /// Note that the root filesystem is transient -- the sled agent is - /// permitted to destroy this dataset each time the zone is initialized. - pub filesystem_pool: Option, - pub zone_type: OmicronZoneType, - // Use `InstallDataset` if this field is not present in a deserialized - // blueprint or ledger. - #[serde(default = "OmicronZoneImageSource::deserialize_default")] - pub image_source: OmicronZoneImageSource, -} - -impl IdOrdItem for OmicronZoneConfig { - type Key<'a> = OmicronZoneUuid; - - fn key(&self) -> Self::Key<'_> { - self.id - } - - id_upcast!(); -} - -impl OmicronZoneConfig { - /// Returns the underlay IP address associated with this zone. - /// - /// Assumes all zone have exactly one underlay IP address (which is - /// currently true). - pub fn underlay_ip(&self) -> Ipv6Addr { - self.zone_type.underlay_ip() - } - - pub fn zone_name(&self) -> String { - illumos_utils::running_zone::InstalledZone::get_zone_name( - self.zone_type.kind().zone_prefix(), - Some(self.id), - ) - } - - pub fn dataset_name(&self) -> Option { - self.zone_type.dataset_name() - } -} - -/// Describes a persistent ZFS dataset associated with an Omicron zone -#[derive( - Clone, - Debug, - Deserialize, - Serialize, - JsonSchema, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - Diffable, -)] -pub struct OmicronZoneDataset { - pub pool_name: ZpoolName, -} - -/// Describes what kind of zone this is (i.e., what component is running in it) -/// as well as any type-specific configuration -#[derive( - Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, -)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum OmicronZoneType { - BoundaryNtp { - address: SocketAddrV6, - ntp_servers: Vec, - dns_servers: Vec, - domain: Option, - /// The service vNIC providing outbound connectivity using OPTE. - nic: NetworkInterface, - /// The SNAT configuration for outbound connections. - snat_cfg: SourceNatConfigGeneric, - }, - - /// Type of clickhouse zone used for a single node clickhouse deployment - Clickhouse { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - /// A zone used to run a Clickhouse Keeper node - /// - /// Keepers are only used in replicated clickhouse setups - ClickhouseKeeper { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - /// A zone used to run a Clickhouse Server in a replicated deployment - ClickhouseServer { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - CockroachDb { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - - Crucible { - address: SocketAddrV6, - dataset: OmicronZoneDataset, - }, - CruciblePantry { - address: SocketAddrV6, - }, - ExternalDns { - dataset: OmicronZoneDataset, - /// The address at which the external DNS server API is reachable. - http_address: SocketAddrV6, - /// The address at which the external DNS server is reachable. - dns_address: SocketAddr, - /// The service vNIC providing external connectivity using OPTE. - nic: NetworkInterface, - }, - InternalDns { - dataset: OmicronZoneDataset, - http_address: SocketAddrV6, - dns_address: SocketAddrV6, - /// The addresses in the global zone which should be created - /// - /// For the DNS service, which exists outside the sleds's typical subnet - /// - adding an address in the GZ is necessary to allow inter-zone - /// traffic routing. - gz_address: Ipv6Addr, - - /// The address is also identified with an auxiliary bit of information - /// to ensure that the created global zone address can have a unique - /// name. - gz_address_index: u32, - }, - InternalNtp { - address: SocketAddrV6, - }, - Nexus { - /// The address at which the internal nexus server is reachable. - internal_address: SocketAddrV6, - /// The port at which the internal lockstep server is reachable. This - /// shares the same IP address with `internal_address`. - #[serde(default = "default_nexus_lockstep_port")] - lockstep_port: u16, - /// The address at which the external nexus server is reachable. - external_ip: IpAddr, - /// The service vNIC providing external connectivity using OPTE. - nic: NetworkInterface, - /// Whether Nexus's external endpoint should use TLS - external_tls: bool, - /// External DNS servers Nexus can use to resolve external hosts. - external_dns_servers: Vec, - }, - Oximeter { - address: SocketAddrV6, - }, -} - -impl OmicronZoneType { - /// Returns the [`ZoneKind`] corresponding to this variant. - pub fn kind(&self) -> ZoneKind { - match self { - OmicronZoneType::BoundaryNtp { .. } => ZoneKind::BoundaryNtp, - OmicronZoneType::Clickhouse { .. } => ZoneKind::Clickhouse, - OmicronZoneType::ClickhouseKeeper { .. } => { - ZoneKind::ClickhouseKeeper - } - OmicronZoneType::ClickhouseServer { .. } => { - ZoneKind::ClickhouseServer - } - OmicronZoneType::CockroachDb { .. } => ZoneKind::CockroachDb, - OmicronZoneType::Crucible { .. } => ZoneKind::Crucible, - OmicronZoneType::CruciblePantry { .. } => ZoneKind::CruciblePantry, - OmicronZoneType::ExternalDns { .. } => ZoneKind::ExternalDns, - OmicronZoneType::InternalDns { .. } => ZoneKind::InternalDns, - OmicronZoneType::InternalNtp { .. } => ZoneKind::InternalNtp, - OmicronZoneType::Nexus { .. } => ZoneKind::Nexus, - OmicronZoneType::Oximeter { .. } => ZoneKind::Oximeter, - } - } - - /// Does this zone require time synchronization before it is initialized?" - /// - /// This function is somewhat conservative - the set of services - /// that can be launched before timesync has completed is intentionally kept - /// small, since it would be easy to add a service that expects time to be - /// reasonably synchronized. - pub fn requires_timesync(&self) -> bool { - match self { - // These zones can be initialized and started before time has been - // synchronized. For the NTP zones, this should be self-evident -- - // we need the NTP zone to actually perform time synchronization! - // - // The DNS zone is a bit of an exception here, since the NTP zone - // itself may rely on DNS lookups as a dependency. - OmicronZoneType::BoundaryNtp { .. } - | OmicronZoneType::InternalNtp { .. } - | OmicronZoneType::InternalDns { .. } => false, - _ => true, - } - } - - /// Returns the underlay IP address associated with this zone. - /// - /// Assumes all zone have exactly one underlay IP address (which is - /// currently true). - pub fn underlay_ip(&self) -> Ipv6Addr { - match self { - OmicronZoneType::BoundaryNtp { address, .. } - | OmicronZoneType::Clickhouse { address, .. } - | OmicronZoneType::ClickhouseKeeper { address, .. } - | OmicronZoneType::ClickhouseServer { address, .. } - | OmicronZoneType::CockroachDb { address, .. } - | OmicronZoneType::Crucible { address, .. } - | OmicronZoneType::CruciblePantry { address } - | OmicronZoneType::ExternalDns { http_address: address, .. } - | OmicronZoneType::InternalNtp { address } - | OmicronZoneType::Nexus { internal_address: address, .. } - | OmicronZoneType::Oximeter { address } => *address.ip(), - OmicronZoneType::InternalDns { - http_address: address, - dns_address, - .. - } => { - // InternalDns is the only variant that carries two - // `SocketAddrV6`s that are both on the underlay network. We - // expect these to have the same IP address. - debug_assert_eq!(address.ip(), dns_address.ip()); - *address.ip() - } - } - } - - /// Identifies whether this is an NTP zone - pub fn is_ntp(&self) -> bool { - match self { - OmicronZoneType::BoundaryNtp { .. } - | OmicronZoneType::InternalNtp { .. } => true, - - OmicronZoneType::Clickhouse { .. } - | OmicronZoneType::ClickhouseKeeper { .. } - | OmicronZoneType::ClickhouseServer { .. } - | OmicronZoneType::CockroachDb { .. } - | OmicronZoneType::Crucible { .. } - | OmicronZoneType::CruciblePantry { .. } - | OmicronZoneType::ExternalDns { .. } - | OmicronZoneType::InternalDns { .. } - | OmicronZoneType::Nexus { .. } - | OmicronZoneType::Oximeter { .. } => false, - } - } - - /// Identifies whether this is a boundary NTP zone - pub fn is_boundary_ntp(&self) -> bool { - matches!(self, OmicronZoneType::BoundaryNtp { .. }) - } - - /// Identifies whether this is a Nexus zone - pub fn is_nexus(&self) -> bool { - match self { - OmicronZoneType::Nexus { .. } => true, - - OmicronZoneType::BoundaryNtp { .. } - | OmicronZoneType::InternalNtp { .. } - | OmicronZoneType::Clickhouse { .. } - | OmicronZoneType::ClickhouseKeeper { .. } - | OmicronZoneType::ClickhouseServer { .. } - | OmicronZoneType::CockroachDb { .. } - | OmicronZoneType::Crucible { .. } - | OmicronZoneType::CruciblePantry { .. } - | OmicronZoneType::ExternalDns { .. } - | OmicronZoneType::InternalDns { .. } - | OmicronZoneType::Oximeter { .. } => false, - } - } - - /// Identifies whether this a Crucible (not Crucible pantry) zone - pub fn is_crucible(&self) -> bool { - match self { - OmicronZoneType::Crucible { .. } => true, - - OmicronZoneType::BoundaryNtp { .. } - | OmicronZoneType::InternalNtp { .. } - | OmicronZoneType::Clickhouse { .. } - | OmicronZoneType::ClickhouseKeeper { .. } - | OmicronZoneType::ClickhouseServer { .. } - | OmicronZoneType::CockroachDb { .. } - | OmicronZoneType::CruciblePantry { .. } - | OmicronZoneType::ExternalDns { .. } - | OmicronZoneType::InternalDns { .. } - | OmicronZoneType::Nexus { .. } - | OmicronZoneType::Oximeter { .. } => false, - } - } - - /// This zone's external IP - pub fn external_ip(&self) -> Option { - match self { - OmicronZoneType::Nexus { external_ip, .. } => Some(*external_ip), - OmicronZoneType::ExternalDns { dns_address, .. } => { - Some(dns_address.ip()) - } - OmicronZoneType::BoundaryNtp { snat_cfg, .. } => Some(snat_cfg.ip), - - OmicronZoneType::InternalNtp { .. } - | OmicronZoneType::Clickhouse { .. } - | OmicronZoneType::ClickhouseKeeper { .. } - | OmicronZoneType::ClickhouseServer { .. } - | OmicronZoneType::CockroachDb { .. } - | OmicronZoneType::Crucible { .. } - | OmicronZoneType::CruciblePantry { .. } - | OmicronZoneType::InternalDns { .. } - | OmicronZoneType::Oximeter { .. } => None, - } - } - - /// The service vNIC providing external connectivity to this zone - pub fn service_vnic(&self) -> Option<&NetworkInterface> { - match self { - OmicronZoneType::Nexus { nic, .. } - | OmicronZoneType::ExternalDns { nic, .. } - | OmicronZoneType::BoundaryNtp { nic, .. } => Some(nic), - - OmicronZoneType::InternalNtp { .. } - | OmicronZoneType::Clickhouse { .. } - | OmicronZoneType::ClickhouseKeeper { .. } - | OmicronZoneType::ClickhouseServer { .. } - | OmicronZoneType::CockroachDb { .. } - | OmicronZoneType::Crucible { .. } - | OmicronZoneType::CruciblePantry { .. } - | OmicronZoneType::InternalDns { .. } - | OmicronZoneType::Oximeter { .. } => None, - } - } - - /// If this kind of zone has an associated dataset, return the dataset's - /// name. Otherwise, return `None`. - pub fn dataset_name(&self) -> Option { - let (dataset, dataset_kind) = match self { - OmicronZoneType::BoundaryNtp { .. } - | OmicronZoneType::InternalNtp { .. } - | OmicronZoneType::Nexus { .. } - | OmicronZoneType::Oximeter { .. } - | OmicronZoneType::CruciblePantry { .. } => None, - OmicronZoneType::Clickhouse { dataset, .. } => { - Some((dataset, DatasetKind::Clickhouse)) - } - OmicronZoneType::ClickhouseKeeper { dataset, .. } => { - Some((dataset, DatasetKind::ClickhouseKeeper)) - } - OmicronZoneType::ClickhouseServer { dataset, .. } => { - Some((dataset, DatasetKind::ClickhouseServer)) - } - OmicronZoneType::CockroachDb { dataset, .. } => { - Some((dataset, DatasetKind::Cockroach)) - } - OmicronZoneType::Crucible { dataset, .. } => { - Some((dataset, DatasetKind::Crucible)) - } - OmicronZoneType::ExternalDns { dataset, .. } => { - Some((dataset, DatasetKind::ExternalDns)) - } - OmicronZoneType::InternalDns { dataset, .. } => { - Some((dataset, DatasetKind::InternalDns)) - } - }?; - - Some(DatasetName::new(dataset.pool_name, dataset_kind)) - } -} - -fn default_nexus_lockstep_port() -> u16 { - omicron_common::address::NEXUS_LOCKSTEP_PORT -} - /// Like [`OmicronZoneType`], but without any associated data. /// /// This enum is meant to correspond exactly 1:1 with `OmicronZoneType`. @@ -1719,111 +1173,235 @@ impl ZoneKind { } } -/// Where Sled Agent should get the image for a zone. +// Used for schemars to be able to be used with camino: +// See https://github.com/camino-rs/camino/issues/91#issuecomment-2027908513 +fn path_schema(generator: &mut SchemaGenerator) -> Schema { + let mut schema: SchemaObject = ::json_schema(generator).into(); + schema.format = Some("Utf8PathBuf".to_owned()); + schema.into() +} + +/// Identity and basic status information about this sled agent +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct Inventory { + pub sled_id: SledUuid, + pub sled_agent_address: SocketAddrV6, + pub sled_role: SledRole, + pub baseboard: Baseboard, + pub usable_hardware_threads: u32, + pub usable_physical_ram: ByteCount, + pub cpu_family: SledCpuFamily, + pub reservoir_size: ByteCount, + pub disks: Vec, + pub zpools: Vec, + pub datasets: Vec, + pub ledgered_sled_config: Option, + pub reconciler_status: ConfigReconcilerInventoryStatus, + pub last_reconciliation: Option, + pub zone_image_resolver: ZoneImageResolverInventory, +} + +/// Describes the set of Reconfigurator-managed configuration elements of a sled +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq)] +pub struct OmicronSledConfig { + pub generation: Generation, + // Serialize and deserialize disks, datasets, and zones as maps for + // backwards compatibility. Newer IdOrdMaps should not use IdOrdMapAsMap. + #[serde( + with = "iddqd::id_ord_map::IdOrdMapAsMap::" + )] + pub disks: IdOrdMap, + #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] + pub datasets: IdOrdMap, + #[serde(with = "iddqd::id_ord_map::IdOrdMapAsMap::")] + pub zones: IdOrdMap, + pub remove_mupdate_override: Option, + #[serde(default = "HostPhase2DesiredSlots::current_contents")] + pub host_phase_2: HostPhase2DesiredSlots, +} + +/// Describes one Omicron-managed zone running on a sled #[derive( - Clone, - Debug, - Deserialize, - Serialize, - JsonSchema, - PartialEq, - Eq, - Hash, - PartialOrd, - Ord, - Diffable, + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, )] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum OmicronZoneImageSource { - /// This zone's image source is whatever happens to be on the sled's - /// "install" dataset. - /// - /// This is whatever was put in place at the factory or by the latest - /// MUPdate. The image used here can vary by sled and even over time (if the - /// sled gets MUPdated again). - /// - /// Historically, this was the only source for zone images. In an system - /// with automated control-plane-driven update we expect to only use this - /// variant in emergencies where the system had to be recovered via MUPdate. - InstallDataset, - /// This zone's image source is the artifact matching this hash from the TUF - /// artifact store (aka "TUF repo depot"). +pub struct OmicronZoneConfig { + pub id: OmicronZoneUuid, + + /// The pool on which we'll place this zone's root filesystem. /// - /// This originates from TUF repos uploaded to Nexus which are then - /// replicated out to all sleds. - Artifact { hash: ArtifactHash }, + /// Note that the root filesystem is transient -- the sled agent is + /// permitted to destroy this dataset each time the zone is initialized. + pub filesystem_pool: Option, + pub zone_type: OmicronZoneType, + // Use `InstallDataset` if this field is not present in a deserialized + // blueprint or ledger. + #[serde(default = "OmicronZoneImageSource::deserialize_default")] + pub image_source: OmicronZoneImageSource, } -impl OmicronZoneImageSource { - /// Return the artifact hash used for the zone image, if the zone's image - /// source is from the artifact store. - pub fn artifact_hash(&self) -> Option { - if let OmicronZoneImageSource::Artifact { hash } = self { - Some(*hash) - } else { - None - } - } +impl IdOrdItem for OmicronZoneConfig { + type Key<'a> = OmicronZoneUuid; - // See `OmicronZoneConfig`. This is a separate function instead of being - // `impl Default` because we don't want to accidentally use this default - // outside of `serde(default)`. - pub fn deserialize_default() -> Self { - OmicronZoneImageSource::InstallDataset + fn key(&self) -> Self::Key<'_> { + self.id } + + id_upcast!(); } -#[cfg(test)] -mod tests { - use omicron_common::api::external::Name; - use strum::IntoEnumIterator; +/// Describes what kind of zone this is (i.e., what component is running in it) +/// as well as any type-specific configuration. +/// +/// Note: This version does NOT have `lockstep_port` in the Nexus variant. +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum OmicronZoneType { + BoundaryNtp { + address: SocketAddrV6, + ntp_servers: Vec, + dns_servers: Vec, + domain: Option, + /// The service vNIC providing outbound connectivity using OPTE. + nic: NetworkInterface, + /// The SNAT configuration for outbound connections. + snat_cfg: SourceNatConfig, + }, - use super::*; + /// Type of clickhouse zone used for a single node clickhouse deployment + Clickhouse { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, - #[test] - fn test_name_prefixes() { - for zone_kind in ZoneKind::iter() { - let name_prefix = zone_kind.name_prefix(); - name_prefix.parse::().unwrap_or_else(|e| { - panic!( - "failed to parse name prefix {:?} for zone kind {:?}: {}", - name_prefix, zone_kind, e - ); - }); - } - } + /// A zone used to run a Clickhouse Keeper node + /// + /// Keepers are only used in replicated clickhouse setups + ClickhouseKeeper { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, - #[test] - fn test_zone_prefix_matches_artifact_in_install_dataset() { - for zone_kind in ZoneKind::iter() { - let zone_prefix = zone_kind.zone_prefix(); - let expected_artifact = format!("{zone_prefix}.tar.gz"); - assert_eq!( - expected_artifact, - zone_kind.artifact_in_install_dataset() - ); - } - } + /// A zone used to run a Clickhouse Server in a replicated deployment + ClickhouseServer { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, - #[test] - fn test_artifact_id_to_install_dataset_file() { - for zone_kind in ZoneKind::iter() { - let artifact_id_name = zone_kind.artifact_id_name(); - let expected_file = zone_kind.artifact_in_install_dataset(); - assert_eq!( - Some(expected_file), - ZoneKind::artifact_id_name_to_install_dataset_file( - artifact_id_name - ) - ); - } - } + CockroachDb { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + + Crucible { + address: SocketAddrV6, + dataset: OmicronZoneDataset, + }, + CruciblePantry { + address: SocketAddrV6, + }, + ExternalDns { + dataset: OmicronZoneDataset, + /// The address at which the external DNS server API is reachable. + http_address: SocketAddrV6, + /// The address at which the external DNS server is reachable. + dns_address: SocketAddr, + /// The service vNIC providing external connectivity using OPTE. + nic: NetworkInterface, + }, + InternalDns { + dataset: OmicronZoneDataset, + http_address: SocketAddrV6, + dns_address: SocketAddrV6, + /// The addresses in the global zone which should be created + /// + /// For the DNS service, which exists outside the sleds's typical subnet + /// - adding an address in the GZ is necessary to allow inter-zone + /// traffic routing. + gz_address: Ipv6Addr, + + /// The address is also identified with an auxiliary bit of information + /// to ensure that the created global zone address can have a unique + /// name. + gz_address_index: u32, + }, + InternalNtp { + address: SocketAddrV6, + }, + /// Note: This variant does NOT have `lockstep_port` (added in v4). + Nexus { + /// The address at which the internal nexus server is reachable. + internal_address: SocketAddrV6, + /// The address at which the external nexus server is reachable. + external_ip: IpAddr, + /// The service vNIC providing external connectivity using OPTE. + nic: NetworkInterface, + /// Whether Nexus's external endpoint should use TLS + external_tls: bool, + /// External DNS servers Nexus can use to resolve external hosts. + external_dns_servers: Vec, + }, + Oximeter { + address: SocketAddrV6, + }, } -// Used for schemars to be able to be used with camino: -// See https://github.com/camino-rs/camino/issues/91#issuecomment-2027908513 -fn path_schema(generator: &mut SchemaGenerator) -> Schema { - let mut schema: SchemaObject = ::json_schema(generator).into(); - schema.format = Some("Utf8PathBuf".to_owned()); - schema.into() +/// Describes the last attempt made by the sled-agent-config-reconciler to +/// reconcile the current sled config against the actual state of the sled. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(rename_all = "snake_case")] +pub struct ConfigReconcilerInventory { + pub last_reconciled_config: OmicronSledConfig, + pub external_disks: + BTreeMap, + pub datasets: BTreeMap, + pub orphaned_datasets: IdOrdMap, + pub zones: BTreeMap, + pub boot_partitions: BootPartitionContents, + /// The result of removing the mupdate override file on disk. + /// + /// `None` if `remove_mupdate_override` was not provided in the sled config. + pub remove_mupdate_override: Option, +} + +/// Status of the sled-agent-config-reconciler task. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, JsonSchema, Serialize)] +#[serde(tag = "status", rename_all = "snake_case")] +pub enum ConfigReconcilerInventoryStatus { + /// The reconciler task has not yet run for the first time since sled-agent + /// started. + NotYetRun, + /// The reconciler task is actively running. + Running { + config: Box, + started_at: DateTime, + running_for: Duration, + }, + /// The reconciler task is currently idle, but previously did complete a + /// reconciliation attempt. + /// + /// This variant does not include the `OmicronSledConfig` used in the last + /// attempt, because that's always available via + /// [`ConfigReconcilerInventory::last_reconciled_config`]. + Idle { completed_at: DateTime, ran_for: Duration }, +} + +/// Describes the set of Omicron-managed zones running on a sled +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +pub struct OmicronZonesConfig { + /// generation number of this configuration + /// + /// This generation number is owned by the control plane (i.e., RSS or + /// Nexus, depending on whether RSS-to-Nexus handoff has happened). It + /// should not be bumped within Sled Agent. + /// + /// Sled Agent rejects attempts to set the configuration to a generation + /// older than the one it's currently running. + pub generation: Generation, + + /// list of running zones + pub zones: Vec, } diff --git a/sled-agent/types/versions/src/initial/mod.rs b/sled-agent/types/versions/src/initial/mod.rs new file mode 100644 index 00000000000..e3dd7c4059e --- /dev/null +++ b/sled-agent/types/versions/src/initial/mod.rs @@ -0,0 +1,17 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `INITIAL` of the Sled Agent API. + +pub mod artifact; +pub mod bootstore; +pub mod debug; +pub mod diagnostics; +pub mod disk; +pub mod early_networking; +pub mod instance; +pub mod inventory; +pub mod sled; +pub mod support_bundle; +pub mod zone_bundle; diff --git a/sled-agent/types/versions/src/initial/sled.rs b/sled-agent/types/versions/src/initial/sled.rs new file mode 100644 index 00000000000..a678b90816b --- /dev/null +++ b/sled-agent/types/versions/src/initial/sled.rs @@ -0,0 +1,263 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Sled-related types for the Sled Agent API. + +use std::net::{Ipv6Addr, SocketAddrV6}; + +use async_trait::async_trait; +use daft::Diffable; +use omicron_common::address::{self, Ipv6Subnet, SLED_PREFIX}; +use omicron_common::ledger::Ledgerable; +use omicron_uuid_kinds::SledUuid; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use sha3::{Digest, Sha3_256}; +use uuid::Uuid; + +/// A representation of a Baseboard ID as used in the inventory subsystem +/// This type is essentially the same as a `Baseboard` except it doesn't have a +/// revision or HW type (Gimlet, PC, Unknown). +#[derive( + Clone, + Debug, + Serialize, + Deserialize, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + JsonSchema, + Diffable, +)] +#[daft(leaf)] +pub struct BaseboardId { + /// Oxide Part Number + pub part_number: String, + /// Serial number (unique for a given part number) + pub serial_number: String, +} + +impl std::fmt::Display for BaseboardId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.part_number, self.serial_number) + } +} + +/// A request to Add a given sled after rack initialization has occurred +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +pub struct AddSledRequest { + pub sled_id: BaseboardId, + pub start_request: StartSledAgentRequest, +} + +/// Configuration information for launching a Sled Agent. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +pub struct StartSledAgentRequest { + /// The current generation number of data as stored in CRDB. + /// + /// The initial generation is set during RSS time and then only mutated + /// by Nexus. For now, we don't actually anticipate mutating this data, + /// but we leave open the possiblity. + pub generation: u64, + + // Which version of the data structure do we have. This is to help with + // deserialization and conversion in future updates. + pub schema_version: u32, + + // The actual configuration details + pub body: StartSledAgentRequestBody, +} + +impl StartSledAgentRequest { + pub fn sled_address(&self) -> SocketAddrV6 { + address::get_sled_address(self.body.subnet) + } + + pub fn switch_zone_ip(&self) -> Ipv6Addr { + address::get_switch_zone_address(self.body.subnet) + } + + /// Compute the sha3_256 digest of `self.rack_id` to use as a `salt` + /// for disk encryption. We don't want to include other values that are + /// consistent across sleds as it would prevent us from moving drives + /// between sleds. + pub fn hash_rack_id(&self) -> [u8; 32] { + // We know the unwrap succeeds as a Sha3_256 digest is 32 bytes + Sha3_256::digest(self.body.rack_id.as_bytes()) + .as_slice() + .try_into() + .unwrap() + } +} + +/// This is the actual app level data of `StartSledAgentRequest` +/// +/// We nest it below the "header" of `generation` and `schema_version` so that +/// we can perform partial deserialization of `EarlyNetworkConfig` to only read +/// the header and defer deserialization of the body once we know the schema +/// version. This is possible via the use of [`serde_json::value::RawValue`] in +/// future (post-v1) deserialization paths. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +pub struct StartSledAgentRequestBody { + /// Uuid of the Sled Agent to be created. + pub id: SledUuid, + + /// Uuid of the rack to which this sled agent belongs. + pub rack_id: Uuid, + + /// Use trust quorum for key generation + pub use_trust_quorum: bool, + + /// Is this node an LRTQ learner node? + /// + /// We only put the node into learner mode if `use_trust_quorum` is also + /// true. + pub is_lrtq_learner: bool, + + /// Portion of the IP space to be managed by the Sled Agent. + pub subnet: Ipv6Subnet, +} + +#[derive(Debug, thiserror::Error)] +#[error("Baseboard is of unknown type")] +pub struct UnknownBaseboardError; + +impl TryFrom for BaseboardId { + type Error = UnknownBaseboardError; + + fn try_from( + value: sled_hardware_types::Baseboard, + ) -> Result { + use sled_hardware_types::Baseboard; + match value { + Baseboard::Gimlet { identifier, model, .. } => Ok(BaseboardId { + part_number: model, + serial_number: identifier, + }), + Baseboard::Pc { identifier, model } => Ok(BaseboardId { + part_number: model, + serial_number: identifier, + }), + Baseboard::Unknown => Err(UnknownBaseboardError), + } + } +} + +#[async_trait] +impl Ledgerable for StartSledAgentRequest { + fn is_newer_than(&self, other: &Self) -> bool { + self.generation > other.generation + } + + fn generation_bump(&mut self) { + // DO NOTHING! + // + // Generation bumps must only ever come from nexus and will be encoded + // in the struct itself + } + + // Attempt to deserialize the v1 or v0 version and return + // the v1 version. + fn deserialize( + s: &str, + ) -> Result { + // Try to deserialize the latest version of the data structure (v1). If + // that succeeds we are done. + if let Ok(val) = serde_json::from_str::(s) { + return Ok(val); + } + + // We don't have the latest version. Try to deserialize v0 and then + // convert it to the latest version. + let v0 = serde_json::from_str::(s)?.request; + Ok(v0.into()) + } +} + +/// The version of `StartSledAgentRequest` we originally shipped with. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +pub struct StartSledAgentRequestV0 { + /// Uuid of the Sled Agent to be created. + pub id: SledUuid, + + /// Uuid of the rack to which this sled agent belongs. + pub rack_id: Uuid, + + /// The external NTP servers to use + pub ntp_servers: Vec, + + /// The external DNS servers to use + pub dns_servers: Vec, + + /// Use trust quorum for key generation + pub use_trust_quorum: bool, + + // Note: The order of these fields is load bearing, because we serialize + // `SledAgentRequest`s as toml. `subnet` serializes as a TOML table, so it + // must come after non-table fields. + /// Portion of the IP space to be managed by the Sled Agent. + pub subnet: Ipv6Subnet, +} + +impl From for StartSledAgentRequest { + fn from(v0: StartSledAgentRequestV0) -> Self { + StartSledAgentRequest { + generation: 0, + schema_version: 1, + body: StartSledAgentRequestBody { + id: v0.id, + rack_id: v0.rack_id, + use_trust_quorum: v0.use_trust_quorum, + is_lrtq_learner: false, + subnet: v0.subnet, + }, + } + } +} + +// A wrapper around StartSledAgentRequestV0 that was used +// for the ledger format. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema)] +struct PersistentSledAgentRequest { + request: StartSledAgentRequestV0, +} + +#[cfg(test)] +mod tests { + use std::net::Ipv6Addr; + + use super::*; + + #[test] + fn serialize_start_sled_agent_v0_deserialize_v1() { + let v0 = PersistentSledAgentRequest { + request: StartSledAgentRequestV0 { + id: SledUuid::new_v4(), + rack_id: Uuid::new_v4(), + ntp_servers: vec![String::from("test.pool.example.com")], + dns_servers: vec!["1.1.1.1".parse().unwrap()], + use_trust_quorum: false, + subnet: Ipv6Subnet::new(Ipv6Addr::LOCALHOST), + }, + }; + let serialized = serde_json::to_string(&v0).unwrap(); + let expected = StartSledAgentRequest { + generation: 0, + schema_version: 1, + body: StartSledAgentRequestBody { + id: v0.request.id, + rack_id: v0.request.rack_id, + use_trust_quorum: v0.request.use_trust_quorum, + is_lrtq_learner: false, + subnet: v0.request.subnet, + }, + }; + + let actual: StartSledAgentRequest = + Ledgerable::deserialize(&serialized).unwrap(); + assert_eq!(expected, actual); + } +} diff --git a/sled-agent/types/versions/src/initial/support_bundle.rs b/sled-agent/types/versions/src/initial/support_bundle.rs new file mode 100644 index 00000000000..a9da65e3eaf --- /dev/null +++ b/sled-agent/types/versions/src/initial/support_bundle.rs @@ -0,0 +1,79 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Support bundle types for Sled Agent API v1. + +use omicron_uuid_kinds::{DatasetUuid, SupportBundleUuid, ZpoolUuid}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use tufaceous_artifact::ArtifactHash; + +/// Path parameters for Support Bundle list requests. +#[derive(Deserialize, JsonSchema)] +pub struct SupportBundleListPathParam { + /// The zpool on which this support bundle was provisioned + pub zpool_id: ZpoolUuid, + + /// The dataset on which this support bundle was provisioned + pub dataset_id: DatasetUuid, +} + +/// Path parameters for Support Bundle requests. +#[derive(Deserialize, JsonSchema)] +pub struct SupportBundlePathParam { + /// The zpool on which this support bundle was provisioned + pub zpool_id: ZpoolUuid, + + /// The dataset on which this support bundle was provisioned + pub dataset_id: DatasetUuid, + + /// The ID of the support bundle itself + pub support_bundle_id: SupportBundleUuid, +} + +/// Path parameters for Support Bundle file requests. +#[derive(Deserialize, JsonSchema)] +pub struct SupportBundleFilePathParam { + #[serde(flatten)] + pub parent: SupportBundlePathParam, + + /// The path of the file within the support bundle to query + pub file: String, +} + +/// Query parameters for support bundle transfer. +#[derive(Deserialize, Serialize, JsonSchema)] +pub struct SupportBundleTransferQueryParams { + pub offset: u64, +} + +/// Query parameters for support bundle finalization. +#[derive(Deserialize, Serialize, JsonSchema)] +pub struct SupportBundleFinalizeQueryParams { + pub hash: ArtifactHash, +} + +/// Range request headers. +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +pub struct RangeRequestHeaders { + /// A request to access a portion of the resource, such as `bytes=0-499` + /// + /// See: + pub range: Option, +} + +/// State of a support bundle. +#[derive(Deserialize, Debug, Serialize, JsonSchema, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum SupportBundleState { + Complete, + Incomplete, +} + +/// Metadata about a support bundle. +#[derive(Debug, Deserialize, Serialize, JsonSchema)] +pub struct SupportBundleMetadata { + pub support_bundle_id: SupportBundleUuid, + pub state: SupportBundleState, +} diff --git a/sled-agent/types/versions/src/initial/zone_bundle.rs b/sled-agent/types/versions/src/initial/zone_bundle.rs new file mode 100644 index 00000000000..dd48453502b --- /dev/null +++ b/sled-agent/types/versions/src/initial/zone_bundle.rs @@ -0,0 +1,507 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Zone bundle types for Sled Agent API version 1. + +use std::cmp::Ordering; +use std::collections::HashSet; +use std::time::Duration; + +use chrono::{DateTime, Utc}; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; + +/// Path parameters for zone requests. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ZonePathParam { + /// The name of the zone. + pub zone_name: String, +} + +/// Query parameters for zone bundle list filtering. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct ZoneBundleFilter { + /// An optional substring used to filter zone bundles. + pub filter: Option, +} + +/// Parameters used to update the zone bundle cleanup context. +#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize)] +pub struct CleanupContextUpdate { + /// The new period on which automatic cleanups are run. + pub period: Option, + /// The priority ordering for preserving old zone bundles. + pub priority: Option, + /// The new limit on the underlying dataset quota allowed for bundles. + pub storage_limit: Option, +} + +/// An identifier for a zone bundle. +#[derive( + Clone, + Debug, + Deserialize, + Eq, + Hash, + JsonSchema, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] +pub struct ZoneBundleId { + /// The name of the zone this bundle is derived from. + pub zone_name: String, + /// The ID for this bundle itself. + pub bundle_id: Uuid, +} + +/// The reason or cause for a zone bundle, i.e., why it was created. +// +// NOTE: The ordering of the enum variants is important, and should not be +// changed without careful consideration. +// +// The ordering is used when deciding which bundles to remove automatically. In +// addition to time, the cause is used to sort bundles, so changing the variant +// order will change that priority. +#[derive( + Clone, + Copy, + Debug, + Default, + Deserialize, + Eq, + Hash, + JsonSchema, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] +#[serde(rename_all = "snake_case")] +#[non_exhaustive] +pub enum ZoneBundleCause { + /// Some other, unspecified reason. + #[default] + Other, + /// A zone bundle taken when a sled agent finds a zone that it does not + /// expect to be running. + UnexpectedZone, + /// An instance zone was terminated. + TerminatedInstance, +} + +/// Metadata about a zone bundle. +#[derive( + Clone, + Debug, + Deserialize, + Eq, + Hash, + JsonSchema, + Ord, + PartialEq, + PartialOrd, + Serialize, +)] +pub struct ZoneBundleMetadata { + /// Identifier for this zone bundle + pub id: ZoneBundleId, + /// The time at which this zone bundle was created. + pub time_created: DateTime, + /// A version number for this zone bundle. + pub version: u8, + /// The reason or cause a bundle was created. + pub cause: ZoneBundleCause, +} + +impl ZoneBundleMetadata { + pub const VERSION: u8 = 0; + + /// Create a new set of metadata for the provided zone. + pub fn new(zone_name: &str, cause: ZoneBundleCause) -> Self { + Self { + id: ZoneBundleId { + zone_name: zone_name.to_string(), + bundle_id: Uuid::new_v4(), + }, + time_created: Utc::now(), + version: Self::VERSION, + cause, + } + } +} + +/// A dimension along with bundles can be sorted, to determine priority. +#[derive( + Clone, + Copy, + Debug, + Deserialize, + Eq, + Hash, + JsonSchema, + Serialize, + Ord, + PartialEq, + PartialOrd, +)] +#[serde(rename_all = "snake_case")] +pub enum PriorityDimension { + /// Sorting by time, with older bundles with lower priority. + Time, + /// Sorting by the cause for creating the bundle. + Cause, +} + +/// The priority order for bundles during cleanup. +/// +/// Bundles are sorted along the dimensions in [`PriorityDimension`], with each +/// dimension appearing exactly once. During cleanup, lesser-priority bundles +/// are pruned first, to maintain the dataset quota. Note that bundles are +/// sorted by each dimension in the order in which they appear, with each +/// dimension having higher priority than the next. +#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, PartialEq, Serialize)] +pub struct PriorityOrder([PriorityDimension; PriorityOrder::EXPECTED_SIZE]); + +impl std::ops::Deref for PriorityOrder { + type Target = [PriorityDimension; PriorityOrder::EXPECTED_SIZE]; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Default for PriorityOrder { + fn default() -> Self { + Self::DEFAULT + } +} + +/// Error type for creating a priority order. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum PriorityOrderCreateError { + WrongDimensionCount(usize), + DuplicateFound(PriorityDimension), +} + +impl std::fmt::Display for PriorityOrderCreateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PriorityOrderCreateError::WrongDimensionCount(n) => { + write!( + f, + "expected exactly {} dimensions, found {}", + PriorityOrder::EXPECTED_SIZE, + n + ) + } + PriorityOrderCreateError::DuplicateFound(dim) => { + write!( + f, + "duplicate element found in priority ordering: {:?}", + dim + ) + } + } + } +} + +impl std::error::Error for PriorityOrderCreateError {} + +impl PriorityOrder { + // NOTE: Must match the number of variants in `PriorityDimension`. + pub(crate) const EXPECTED_SIZE: usize = 2; + const DEFAULT: Self = + Self([PriorityDimension::Cause, PriorityDimension::Time]); + + /// Construct a new priority order. + /// + /// This requires that each dimension appear exactly once. + pub fn new( + dims: &[PriorityDimension], + ) -> Result { + if dims.len() != Self::EXPECTED_SIZE { + return Err(PriorityOrderCreateError::WrongDimensionCount( + dims.len(), + )); + } + let mut seen = HashSet::new(); + for dim in dims.iter() { + if !seen.insert(dim) { + return Err(PriorityOrderCreateError::DuplicateFound(*dim)); + } + } + Ok(Self(dims.try_into().unwrap())) + } + + /// Get the priority order as a slice. + pub fn as_slice(&self) -> &[PriorityDimension] { + &self.0 + } + + /// Order zone bundle metadata according to the contained priority. + /// + /// We sort the metadata by each dimension, in the order in which it + /// appears. That means earlier dimensions have higher priority than later + /// ones. + pub fn compare_metadata( + &self, + lhs: &ZoneBundleMetadata, + rhs: &ZoneBundleMetadata, + ) -> Ordering { + for dim in self.0.iter() { + let ord = match dim { + PriorityDimension::Cause => lhs.cause.cmp(&rhs.cause), + PriorityDimension::Time => { + lhs.time_created.cmp(&rhs.time_created) + } + }; + if matches!(ord, Ordering::Equal) { + continue; + } + return ord; + } + Ordering::Equal + } +} + +/// A period on which bundles are automatically cleaned up. +#[derive( + Clone, Copy, Deserialize, JsonSchema, PartialEq, PartialOrd, Serialize, +)] +pub struct CleanupPeriod(Duration); + +impl Default for CleanupPeriod { + fn default() -> Self { + Self(Duration::from_secs(600)) + } +} + +/// Error type for creating a cleanup period. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CleanupPeriodCreateError(pub Duration); + +impl std::fmt::Display for CleanupPeriodCreateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "invalid cleanup period ({:?}): must be between {:?} and {:?}, inclusive", + self.0, + CleanupPeriod::MIN.as_duration(), + CleanupPeriod::MAX.as_duration(), + ) + } +} + +impl std::error::Error for CleanupPeriodCreateError {} + +impl CleanupPeriod { + /// The minimum supported cleanup period. + pub const MIN: Self = Self(Duration::from_secs(60)); + + /// The maximum supported cleanup period. + pub const MAX: Self = Self(Duration::from_secs(60 * 60 * 24)); + + /// Construct a new cleanup period, checking that it's valid. + pub fn new(duration: Duration) -> Result { + if duration >= Self::MIN.as_duration() + && duration <= Self::MAX.as_duration() + { + Ok(Self(duration)) + } else { + Err(CleanupPeriodCreateError(duration)) + } + } + + /// Return the period as a duration. + pub const fn as_duration(&self) -> Duration { + self.0 + } +} + +impl TryFrom for CleanupPeriod { + type Error = CleanupPeriodCreateError; + + fn try_from(duration: Duration) -> Result { + Self::new(duration) + } +} + +impl std::fmt::Debug for CleanupPeriod { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.fmt(f) + } +} + +/// The limit on space allowed for zone bundles, as a percentage of the overall +/// dataset's quota. +#[derive( + Clone, + Copy, + Debug, + Deserialize, + JsonSchema, + PartialEq, + PartialOrd, + Serialize, +)] +pub struct StorageLimit(u8); + +impl std::fmt::Display for StorageLimit { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}%", self.as_u8()) + } +} + +impl Default for StorageLimit { + fn default() -> Self { + StorageLimit(25) + } +} + +/// Error type for creating a storage limit. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StorageLimitCreateError(pub u8); + +impl std::fmt::Display for StorageLimitCreateError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "invalid storage limit ({}): must be expressed as a percentage in ({}, {}]", + self.0, + StorageLimit::MIN.0, + StorageLimit::MAX.0, + ) + } +} + +impl std::error::Error for StorageLimitCreateError {} + +impl StorageLimit { + /// Minimum percentage of dataset quota supported. + pub const MIN: Self = Self(0); + + /// Maximum percentage of dataset quota supported. + pub const MAX: Self = Self(50); + + /// Construct a new limit allowed for zone bundles. + /// + /// This should be expressed as a percentage, in the range (Self::MIN, + /// Self::MAX]. + pub const fn new(percentage: u8) -> Result { + if percentage > Self::MIN.0 && percentage <= Self::MAX.0 { + Ok(Self(percentage)) + } else { + Err(StorageLimitCreateError(percentage)) + } + } + + /// Return the contained quota percentage. + pub const fn as_u8(&self) -> u8 { + self.0 + } + + // Compute the number of bytes available from a dataset quota, in bytes. + pub const fn bytes_available(&self, dataset_quota: u64) -> u64 { + (dataset_quota * self.as_u8() as u64) / 100 + } +} + +/// The portion of a debug dataset used for zone bundles. +#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)] +pub struct BundleUtilization { + /// The total dataset quota, in bytes. + pub dataset_quota: u64, + /// The total number of bytes available for zone bundles. + /// + /// This is `dataset_quota` multiplied by the context's storage limit. + pub bytes_available: u64, + /// Total bundle usage, in bytes. + pub bytes_used: u64, +} + +/// Context provided for the zone bundle cleanup task. +#[derive( + Clone, Copy, Debug, Default, Deserialize, JsonSchema, PartialEq, Serialize, +)] +pub struct CleanupContext { + /// The period on which automatic checks and cleanup is performed. + pub period: CleanupPeriod, + /// The limit on the dataset quota available for zone bundles. + pub storage_limit: StorageLimit, + /// The priority ordering for keeping old bundles. + pub priority: PriorityOrder, +} + +/// The count of bundles / bytes removed during a cleanup operation. +#[derive(Clone, Copy, Debug, Default, Deserialize, JsonSchema, Serialize)] +pub struct CleanupCount { + /// The number of bundles removed. + pub bundles: u64, + /// The number of bytes removed. + pub bytes: u64, +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sort_zone_bundle_cause() { + use ZoneBundleCause::*; + let mut original = [Other, TerminatedInstance, UnexpectedZone]; + let expected = [Other, UnexpectedZone, TerminatedInstance]; + original.sort(); + assert_eq!(original, expected); + } + + #[test] + fn test_priority_dimension() { + assert!(PriorityOrder::new(&[]).is_err()); + assert!(PriorityOrder::new(&[PriorityDimension::Cause]).is_err()); + assert!( + PriorityOrder::new(&[ + PriorityDimension::Cause, + PriorityDimension::Cause + ]) + .is_err() + ); + assert!( + PriorityOrder::new(&[ + PriorityDimension::Cause, + PriorityDimension::Cause, + PriorityDimension::Time + ]) + .is_err() + ); + + assert!( + PriorityOrder::new(&[ + PriorityDimension::Cause, + PriorityDimension::Time + ]) + .is_ok() + ); + assert_eq!( + PriorityOrder::new(PriorityOrder::default().as_slice()).unwrap(), + PriorityOrder::default() + ); + } + + #[test] + fn test_storage_limit_bytes_available() { + let pct = StorageLimit(1); + assert_eq!(pct.bytes_available(100), 1); + assert_eq!(pct.bytes_available(1000), 10); + + let pct = StorageLimit(50); + assert_eq!(pct.bytes_available(100), 50); + assert_eq!(pct.bytes_available(1000), 500); + + // Test non-power of 10. + let pct = StorageLimit(25); + assert_eq!(pct.bytes_available(32768), 8192); + } +} diff --git a/sled-agent/types/versions/src/latest.rs b/sled-agent/types/versions/src/latest.rs new file mode 100644 index 00000000000..23b67460b22 --- /dev/null +++ b/sled-agent/types/versions/src/latest.rs @@ -0,0 +1,165 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Re-exports of the latest versions of all types. + +pub mod artifact { + pub use crate::v1::artifact::ArtifactConfig; + pub use crate::v1::artifact::ArtifactCopyFromDepotBody; + pub use crate::v1::artifact::ArtifactCopyFromDepotResponse; + pub use crate::v1::artifact::ArtifactListResponse; + pub use crate::v1::artifact::ArtifactPathParam; + pub use crate::v1::artifact::ArtifactPutResponse; + pub use crate::v1::artifact::ArtifactQueryParam; +} + +pub mod bootstore { + pub use crate::v1::bootstore::BootstoreStatus; + pub use crate::v1::bootstore::EstablishedConnection; +} + +pub mod dataset { + pub use crate::v9::dataset::LocalStorageDatasetEnsureRequest; + pub use crate::v9::dataset::LocalStoragePathParam; +} + +pub mod debug { + pub use crate::v1::debug::ChickenSwitchDestroyOrphanedDatasets; + + pub use crate::v3::debug::OperatorSwitchZonePolicy; +} + +pub mod diagnostics { + pub use crate::v1::diagnostics::SledDiagnosticsLogsDownloadPathParam; + pub use crate::v1::diagnostics::SledDiagnosticsLogsDownloadPathParm; + pub use crate::v1::diagnostics::SledDiagnosticsLogsDownloadQueryParam; +} + +pub mod disk { + pub use crate::v1::disk::DiskEnsureBody; + pub use crate::v1::disk::DiskPathParam; + pub use crate::v1::disk::DiskStateRequested; + pub use crate::v1::disk::DiskType; + pub use crate::v1::disk::Zpool; +} + +pub mod early_networking { + pub use crate::v1::early_networking::EarlyNetworkConfig; + pub use crate::v1::early_networking::EarlyNetworkConfigBody; +} + +pub mod firewall_rules { + pub use crate::v11::firewall_rules::VpcFirewallRulesEnsureBody; +} + +pub mod instance { + pub use crate::v1::instance::InstanceExternalIpBody; + pub use crate::v1::instance::InstanceMetadata; + pub use crate::v1::instance::InstanceMigrationTargetParams; + pub use crate::v1::instance::VmmIssueDiskSnapshotRequestBody; + pub use crate::v1::instance::VmmIssueDiskSnapshotRequestPathParam; + pub use crate::v1::instance::VmmIssueDiskSnapshotRequestResponse; + pub use crate::v1::instance::VmmPathParam; + pub use crate::v1::instance::VmmPutStateBody; + pub use crate::v1::instance::VmmPutStateResponse; + pub use crate::v1::instance::VmmSpec; + pub use crate::v1::instance::VmmSpecExt; + pub use crate::v1::instance::VmmStateRequested; + pub use crate::v1::instance::VmmUnregisterResponse; + pub use crate::v1::instance::VpcPathParam; + + pub use crate::v7::instance::InstanceMulticastBody; + pub use crate::v7::instance::InstanceMulticastMembership; + + pub use crate::v11::instance::InstanceEnsureBody; + pub use crate::v11::instance::InstanceSledLocalConfig; + + pub use omicron_common::api::internal::shared::ResolvedVpcFirewallRule; +} + +pub mod inventory { + pub use crate::v1::inventory::Baseboard; + pub use crate::v1::inventory::BootImageHeader; + pub use crate::v1::inventory::BootPartitionContents; + pub use crate::v1::inventory::BootPartitionDetails; + pub use crate::v1::inventory::ConfigReconcilerInventoryResult; + pub use crate::v1::inventory::HostPhase2DesiredContents; + pub use crate::v1::inventory::HostPhase2DesiredSlots; + pub use crate::v1::inventory::InventoryDataset; + pub use crate::v1::inventory::InventoryDisk; + pub use crate::v1::inventory::InventoryZpool; + pub use crate::v1::inventory::MupdateOverrideBootInventory; + pub use crate::v1::inventory::MupdateOverrideInventory; + pub use crate::v1::inventory::MupdateOverrideNonBootInventory; + pub use crate::v1::inventory::OmicronZoneDataset; + pub use crate::v1::inventory::OmicronZoneImageSource; + pub use crate::v1::inventory::OrphanedDataset; + pub use crate::v1::inventory::RemoveMupdateOverrideBootSuccessInventory; + pub use crate::v1::inventory::RemoveMupdateOverrideInventory; + pub use crate::v1::inventory::SledCpuFamily; + pub use crate::v1::inventory::SledRole; + pub use crate::v1::inventory::ZoneArtifactInventory; + pub use crate::v1::inventory::ZoneImageResolverInventory; + pub use crate::v1::inventory::ZoneKind; + pub use crate::v1::inventory::ZoneManifestBootInventory; + pub use crate::v1::inventory::ZoneManifestInventory; + pub use crate::v1::inventory::ZoneManifestNonBootInventory; + + pub use crate::v11::inventory::ConfigReconcilerInventory; + pub use crate::v11::inventory::ConfigReconcilerInventoryStatus; + pub use crate::v11::inventory::Inventory; + pub use crate::v11::inventory::OmicronSledConfig; + pub use crate::v11::inventory::OmicronZoneConfig; + pub use crate::v11::inventory::OmicronZoneType; + pub use crate::v11::inventory::OmicronZonesConfig; +} + +pub mod probes { + pub use crate::v10::probes::ExternalIp; + pub use crate::v10::probes::IpKind; + pub use crate::v10::probes::ProbeCreate; + pub use crate::v10::probes::ProbeSet; +} + +pub mod rack_init { + pub use crate::bootstrap_v1::rack_init::RecoverySiloConfig; +} + +pub mod sled { + pub use crate::v1::sled::AddSledRequest; + pub use crate::v1::sled::BaseboardId; + pub use crate::v1::sled::StartSledAgentRequest; + pub use crate::v1::sled::StartSledAgentRequestBody; + pub use crate::v1::sled::UnknownBaseboardError; +} + +pub mod support_bundle { + pub use crate::v1::support_bundle::RangeRequestHeaders; + pub use crate::v1::support_bundle::SupportBundleFilePathParam; + pub use crate::v1::support_bundle::SupportBundleFinalizeQueryParams; + pub use crate::v1::support_bundle::SupportBundleListPathParam; + pub use crate::v1::support_bundle::SupportBundleMetadata; + pub use crate::v1::support_bundle::SupportBundlePathParam; + pub use crate::v1::support_bundle::SupportBundleState; + pub use crate::v1::support_bundle::SupportBundleTransferQueryParams; +} + +pub mod zone_bundle { + pub use crate::v1::zone_bundle::BundleUtilization; + pub use crate::v1::zone_bundle::CleanupContext; + pub use crate::v1::zone_bundle::CleanupContextUpdate; + pub use crate::v1::zone_bundle::CleanupCount; + pub use crate::v1::zone_bundle::CleanupPeriod; + pub use crate::v1::zone_bundle::CleanupPeriodCreateError; + pub use crate::v1::zone_bundle::PriorityDimension; + pub use crate::v1::zone_bundle::PriorityOrder; + pub use crate::v1::zone_bundle::PriorityOrderCreateError; + pub use crate::v1::zone_bundle::StorageLimit; + pub use crate::v1::zone_bundle::StorageLimitCreateError; + pub use crate::v1::zone_bundle::ZoneBundleCause; + pub use crate::v1::zone_bundle::ZoneBundleFilter; + pub use crate::v1::zone_bundle::ZoneBundleId; + pub use crate::v1::zone_bundle::ZoneBundleMetadata; + pub use crate::v1::zone_bundle::ZonePathParam; +} diff --git a/sled-agent/types/versions/src/lib.rs b/sled-agent/types/versions/src/lib.rs new file mode 100644 index 00000000000..7b89e956590 --- /dev/null +++ b/sled-agent/types/versions/src/lib.rs @@ -0,0 +1,50 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Versioned types for the Sled Agent API. +//! +//! # Adding a new API version +//! +//! When adding a new API version N with added or changed types: +//! +//! 1. Create `/mod.rs`, where `` is the lowercase +//! form of the new version's identifier, as defined in the API trait's +//! `api_versions!` macro. +//! +//! 2. Add to the end of this list: +//! +//! ```rust,ignore +//! #[path = "/mod.rs"] +//! pub mod vN; +//! ``` +//! +//! 3. Add your types to the new module, mirroring the module structure from +//! earlier versions. +//! +//! 4. Update `latest.rs` with new and updated types from the new version. +//! +//! For more information, see the [detailed guide] and [RFD 619]. +//! +//! [detailed guide]: https://github.com/oxidecomputer/dropshot-api-manager/blob/main/guides/new-version.md +//! [RFD 619]: https://rfd.shared.oxide.computer/rfd/619 + +#[path = "bootstrap_initial/mod.rs"] +pub mod bootstrap_v1; +pub mod latest; +#[path = "initial/mod.rs"] +pub mod v1; +#[path = "add_dual_stack_shared_network_interfaces/mod.rs"] +pub mod v10; +#[path = "add_dual_stack_external_ip_config/mod.rs"] +pub mod v11; +#[path = "add_switch_zone_operator_policy/mod.rs"] +pub mod v3; +#[path = "add_nexus_lockstep_port_to_inventory/mod.rs"] +pub mod v4; +#[path = "add_probe_put_endpoint/mod.rs"] +pub mod v6; +#[path = "multicast_support/mod.rs"] +pub mod v7; +#[path = "delegate_zvol_to_propolis/mod.rs"] +pub mod v9; diff --git a/sled-agent/api/src/v8.rs b/sled-agent/types/versions/src/multicast_support/instance.rs similarity index 50% rename from sled-agent/api/src/v8.rs rename to sled-agent/types/versions/src/multicast_support/instance.rs index cc18138cd78..1a128c7d9e9 100644 --- a/sled-agent/api/src/v8.rs +++ b/sled-agent/types/versions/src/multicast_support/instance.rs @@ -2,28 +2,23 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -//! Sled agent types that changed from version 8 to version 9 +use std::net::{IpAddr, SocketAddr}; -use omicron_common::api::external; use omicron_common::api::external::Hostname; -use omicron_common::api::internal::nexus::HostIdentifier; use omicron_common::api::internal::nexus::VmmRuntimeState; use omicron_common::api::internal::shared::DhcpConfig; use omicron_common::api::internal::shared::external_ip::v1::SourceNatConfig; use omicron_common::api::internal::shared::network_interface::v1::NetworkInterface; use omicron_uuid_kinds::InstanceUuid; use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use sled_agent_types::instance::InstanceMetadata; -use sled_agent_types::instance::InstanceMulticastMembership; -use sled_agent_types::instance::VmmSpec; -use sled_agent_types::inventory::v9; -use std::collections::HashSet; -use std::net::IpAddr; -use std::net::SocketAddr; +use serde::{Deserialize, Serialize}; use uuid::Uuid; +use crate::v1; +use crate::v1::instance::InstanceMetadata; +use crate::v1::instance::ResolvedVpcFirewallRule; +use crate::v1::instance::VmmSpec; + /// The body of a request to ensure that a instance and VMM are known to a sled /// agent. #[derive(Serialize, Deserialize, JsonSchema)] @@ -56,6 +51,8 @@ pub struct InstanceEnsureBody { /// Describes sled-local configuration that a sled-agent must establish to make /// the instance's virtual hardware fully functional. +/// +/// Added in v7: `multicast_groups` field. #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] pub struct InstanceSledLocalConfig { pub hostname: Hostname, @@ -70,62 +67,51 @@ pub struct InstanceSledLocalConfig { pub dhcp_config: DhcpConfig, } -impl From for v9::InstanceEnsureBody { - fn from(v8: InstanceEnsureBody) -> Self { - Self { - vmm_spec: v8.vmm_spec, - local_config: v8.local_config.into(), - vmm_runtime: v8.vmm_runtime, - instance_id: v8.instance_id, - migration_id: v8.migration_id, - propolis_addr: v8.propolis_addr, - metadata: v8.metadata, - } - } +/// Represents a multicast group membership for an instance. +/// +/// Introduced in v7. +#[derive( + Clone, Debug, Deserialize, Serialize, JsonSchema, PartialEq, Eq, Hash, +)] +pub struct InstanceMulticastMembership { + pub group_ip: IpAddr, + // For Source-Specific Multicast (SSM) + pub sources: Vec, } -impl From for v9::InstanceSledLocalConfig { - fn from(v8: InstanceSledLocalConfig) -> Self { - let firewall_rules = - v8.firewall_rules.into_iter().map(Into::into).collect(); +impl From for InstanceEnsureBody { + fn from(v1: v1::instance::InstanceEnsureBody) -> Self { Self { - hostname: v8.hostname, - nics: v8.nics, - source_nat: v8.source_nat, - ephemeral_ip: v8.ephemeral_ip, - floating_ips: v8.floating_ips, - multicast_groups: v8.multicast_groups, - firewall_rules, - dhcp_config: v8.dhcp_config, - delegated_zvols: vec![], + vmm_spec: v1.vmm_spec, + local_config: v1.local_config.into(), + vmm_runtime: v1.vmm_runtime, + instance_id: v1.instance_id, + migration_id: v1.migration_id, + propolis_addr: v1.propolis_addr, + metadata: v1.metadata, } } } -/// VPC firewall rule after object name resolution has been performed by Nexus -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] -pub struct ResolvedVpcFirewallRule { - pub status: external::VpcFirewallRuleStatus, - pub direction: external::VpcFirewallRuleDirection, - pub targets: Vec, - pub filter_hosts: Option>, - pub filter_ports: Option>, - pub filter_protocols: Option>, - pub action: external::VpcFirewallRuleAction, - pub priority: external::VpcFirewallRulePriority, -} - -impl From for v9::ResolvedVpcFirewallRule { - fn from(v8: ResolvedVpcFirewallRule) -> Self { +impl From for InstanceSledLocalConfig { + fn from(v1: v1::instance::InstanceSledLocalConfig) -> Self { Self { - status: v8.status, - direction: v8.direction, - targets: v8.targets, - filter_hosts: v8.filter_hosts, - filter_ports: v8.filter_ports, - filter_protocols: v8.filter_protocols, - action: v8.action, - priority: v8.priority, + hostname: v1.hostname, + nics: v1.nics, + source_nat: v1.source_nat, + ephemeral_ip: v1.ephemeral_ip, + floating_ips: v1.floating_ips, + multicast_groups: Vec::new(), // Added in v7 + firewall_rules: v1.firewall_rules, + dhcp_config: v1.dhcp_config, } } } + +/// Request body for multicast group operations. +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum InstanceMulticastBody { + Join(InstanceMulticastMembership), + Leave(InstanceMulticastMembership), +} diff --git a/sled-agent/types/versions/src/multicast_support/mod.rs b/sled-agent/types/versions/src/multicast_support/mod.rs new file mode 100644 index 00000000000..d788e51e98b --- /dev/null +++ b/sled-agent/types/versions/src/multicast_support/mod.rs @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Version `MULTICAST_SUPPORT` of the Sled Agent API. +//! +//! This version added multicast_groups to InstanceSledLocalConfig. + +pub mod instance; diff --git a/sled-agent/zone-images-examples/Cargo.toml b/sled-agent/zone-images-examples/Cargo.toml index e99994b7638..3df2339c499 100644 --- a/sled-agent/zone-images-examples/Cargo.toml +++ b/sled-agent/zone-images-examples/Cargo.toml @@ -11,7 +11,6 @@ workspace = true camino.workspace = true camino-tempfile-ext.workspace = true iddqd.workspace = true -nexus-sled-agent-shared.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/sled-agent/zone-images-examples/src/lib.rs b/sled-agent/zone-images-examples/src/lib.rs index 8c631096378..2eeb67cc3ee 100644 --- a/sled-agent/zone-images-examples/src/lib.rs +++ b/sled-agent/zone-images-examples/src/lib.rs @@ -15,13 +15,13 @@ use camino_tempfile_ext::{ prelude::*, }; use iddqd::{IdOrdItem, IdOrdMap, id_upcast}; -use nexus_sled_agent_shared::inventory::ZoneKind; use omicron_common::update::{ MupdateOverrideInfo, OmicronZoneFileMetadata, OmicronZoneManifest, OmicronZoneManifestSource, }; use omicron_uuid_kinds::{InternalZpoolUuid, MupdateOverrideUuid, MupdateUuid}; use sha2::{Digest, Sha256}; +use sled_agent_types::inventory::ZoneKind; use sled_agent_types::zone_images::{ ArcIoError, ArcSerdeJsonError, ArtifactReadResult, InstallMetadataReadError, ZoneManifestArtifactResult, diff --git a/sled-agent/zone-images/Cargo.toml b/sled-agent/zone-images/Cargo.toml index 312c20ca250..6692631df32 100644 --- a/sled-agent/zone-images/Cargo.toml +++ b/sled-agent/zone-images/Cargo.toml @@ -11,7 +11,6 @@ workspace = true camino.workspace = true iddqd.workspace = true illumos-utils.workspace = true -nexus-sled-agent-shared.workspace = true omicron-common.workspace = true omicron-uuid-kinds.workspace = true omicron-workspace-hack.workspace = true diff --git a/sled-agent/zone-images/src/source_resolver.rs b/sled-agent/zone-images/src/source_resolver.rs index 278d6a1f5eb..6f84cb406be 100644 --- a/sled-agent/zone-images/src/source_resolver.rs +++ b/sled-agent/zone-images/src/source_resolver.rs @@ -7,10 +7,10 @@ use crate::mupdate_override::AllMupdateOverrides; use crate::zone_manifest::AllZoneManifests; use camino::Utf8PathBuf; -use nexus_sled_agent_shared::inventory::OmicronZoneImageSource; use omicron_uuid_kinds::MupdateOverrideUuid; use sled_agent_config_reconciler::InternalDisks; use sled_agent_config_reconciler::InternalDisksWithBootDisk; +use sled_agent_types::inventory::OmicronZoneImageSource; use sled_agent_types::zone_images::RemoveMupdateOverrideResult; use sled_agent_types::zone_images::ResolverStatus; use slog::o; @@ -120,13 +120,11 @@ mod tests { use camino_tempfile_ext::prelude::*; use dropshot::{ConfigLogging, ConfigLoggingLevel, test_util::LogContext}; - use nexus_sled_agent_shared::inventory::{ - HostPhase2DesiredContents, ZoneKind, - }; use omicron_common::zone_images::ZoneImageFileSource; use sled_agent_config_reconciler::{ HostPhase2PreparedContents, ResolverStatusExt, }; + use sled_agent_types::inventory::{HostPhase2DesiredContents, ZoneKind}; use sled_agent_types::zone_images::{ MupdateOverrideReadError, OmicronZoneFileSource, OmicronZoneImageLocation, RAMDISK_IMAGE_PATH, ZoneImageLocationError,