From bda5a2236775792fc5c64b5dd2c894e1e5d0e788 Mon Sep 17 00:00:00 2001 From: Nils Nieuwejaar Date: Sat, 15 Jun 2024 15:19:25 +0000 Subject: [PATCH] tests pass --- sled-agent/src/bootstrap/early_networking.rs | 100 ++++++++---- sled-agent/src/bootstrap/params.rs | 143 +++++++++++++++--- sled-agent/src/config.rs | 7 +- sled-agent/src/rack_setup/config.rs | 6 +- sled-agent/src/rack_setup/plan/sled.rs | 44 +++++- sled-agent/tests/data/early_network_blobs.txt | 4 +- .../tests/integration_tests/early_network.rs | 7 +- .../madrid-rss-sled-plan.json | 2 +- 8 files changed, 248 insertions(+), 65 deletions(-) diff --git a/sled-agent/src/bootstrap/early_networking.rs b/sled-agent/src/bootstrap/early_networking.rs index 40ee0d6c48..cdad6b955c 100644 --- a/sled-agent/src/bootstrap/early_networking.rs +++ b/sled-agent/src/bootstrap/early_networking.rs @@ -42,6 +42,7 @@ use serde::{Deserialize, Serialize}; use slog::Logger; use std::collections::{HashMap, HashSet}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV6}; +use std::str::FromStr; use std::time::{Duration, Instant}; use thiserror::Error; @@ -796,7 +797,42 @@ pub struct EarlyNetworkConfig { 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:?}"), + }; + 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( @@ -822,17 +858,12 @@ impl EarlyNetworkConfig { }; match serde_json::from_slice::(&config.blob) { - Ok(val) => { + Ok(v1) => { // Convert from v1 to v2 return Ok(EarlyNetworkConfig { - generation: val.generation, - schema_version: 2, - body: EarlyNetworkConfigBody { - ntp_servers: val.body.ntp_servers, - rack_network_config: val.body.rack_network_config.map( - |v1_config| RackNetworkConfigV1::to_v2(v1_config), - ), - }, + generation: v1.generation, + schema_version: EarlyNetworkConfig::schema_version(), + body: v1.body.into(), }); } Err(error) => { @@ -918,6 +949,17 @@ struct EarlyNetworkConfigBodyV1 { 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 /// /// @@ -962,8 +1004,8 @@ impl RackNetworkConfigV0 { /// Deprecated, use PortConfigV2 instead. Cannot actually deprecate due to /// -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] -struct PortConfigV1 { +#[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 @@ -984,20 +1026,20 @@ struct PortConfigV1 { } impl From for PortConfigV2 { - fn from(value: PortConfigV1) -> Self { + fn from(v1: PortConfigV1) -> Self { PortConfigV2 { - routes: value.routes.clone(), - addresses: value + routes: v1.routes.clone(), + addresses: v1 .addresses .iter() .map(|a| UplinkAddressConfig { address: *a, vlan_id: None }) .collect(), - switch: value.switch, - port: value.port, - uplink_port_speed: value.uplink_port_speed, - uplink_port_fec: value.uplink_port_fec, - bgp_peers: value.bgp_peers.clone(), - autoneg: false, + switch: v1.switch, + port: v1.port, + uplink_port_speed: v1.uplink_port_speed, + uplink_port_fec: v1.uplink_port_fec, + bgp_peers: v1.bgp_peers.clone(), + autoneg: v1.autoneg, } } } @@ -1051,8 +1093,8 @@ impl From for PortConfigV2 { /// 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, Deserialize, Serialize, JsonSchema)] -struct RackNetworkConfigV1 { +#[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 @@ -1068,14 +1110,8 @@ struct RackNetworkConfigV1 { pub bfd: Vec, } -impl RackNetworkConfigV1 { - /// Convert from `RackNetworkConfigV1` to `RackNetworkConfigV2` - /// - /// We cannot use `From for `RackNetworkConfigV1` - /// because the `rack_subnet` field does not exist in `RackNetworkConfigV0` - /// and must be passed in from the `EarlyNetworkConfigV0` struct which - /// contains the `RackNetworkConfivV0` struct. - pub fn to_v2(v1: RackNetworkConfigV1) -> RackNetworkConfigV2 { +impl From for RackNetworkConfigV2 { + fn from(v1: RackNetworkConfigV1) -> Self { RackNetworkConfigV2 { rack_subnet: v1.rack_subnet, infra_ip_first: v1.infra_ip_first, @@ -1161,7 +1197,7 @@ mod tests { let uplink = v0_rack_network_config.uplinks[0].clone(); let expected = EarlyNetworkConfig { generation: 1, - schema_version: 2, + schema_version: EarlyNetworkConfig::schema_version(), body: EarlyNetworkConfigBody { ntp_servers: v0.ntp_servers.clone(), rack_network_config: Some(RackNetworkConfigV2 { @@ -1245,7 +1281,7 @@ mod tests { let port = v1_rack_network_config.ports[0].clone(); let expected = EarlyNetworkConfig { generation: 1, - schema_version: 2, + schema_version: EarlyNetworkConfig::schema_version(), body: EarlyNetworkConfigBody { ntp_servers: v1.body.ntp_servers.clone(), rack_network_config: Some(RackNetworkConfigV2 { diff --git a/sled-agent/src/bootstrap/params.rs b/sled-agent/src/bootstrap/params.rs index e458900c53..1876ca41b9 100644 --- a/sled-agent/src/bootstrap/params.rs +++ b/sled-agent/src/bootstrap/params.rs @@ -4,6 +4,7 @@ //! Request types for the bootstrap agent +use crate::bootstrap::early_networking::RackNetworkConfigV1; use anyhow::{bail, Result}; use async_trait::async_trait; use omicron_common::address::{self, Ipv6Subnet, SLED_PREFIX}; @@ -28,6 +29,62 @@ pub enum BootstrapAddressDiscovery { OnlyThese { addrs: BTreeSet }, } +/// This is a deprecated format, maintained to allow importing from older +/// versions. +#[derive(Clone, Deserialize)] +struct UnvalidatedRackInitializeRequestV1 { + trust_quorum_peers: Option>, + bootstrap_discovery: BootstrapAddressDiscovery, + ntp_servers: Vec, + dns_servers: Vec, + internal_services_ip_pool_ranges: Vec, + external_dns_ips: Vec, + external_dns_zone_name: String, + external_certificates: Vec, + recovery_silo: RecoverySiloConfig, + rack_network_config: RackNetworkConfigV1, + #[serde(default = "default_allowed_source_ips")] + allowed_source_ips: AllowedSourceIps, +} + +/// This is a deprecated format, maintained to allow importing from older +/// versions. +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(try_from = "UnvalidatedRackInitializeRequestV1")] +pub struct RackInitializeRequestV1 { + pub trust_quorum_peers: Option>, + pub bootstrap_discovery: BootstrapAddressDiscovery, + pub ntp_servers: Vec, + pub dns_servers: Vec, + pub internal_services_ip_pool_ranges: Vec, + pub external_dns_ips: Vec, + pub external_dns_zone_name: String, + pub external_certificates: Vec, + pub recovery_silo: RecoverySiloConfig, + pub rack_network_config: RackNetworkConfigV1, + #[serde(default = "default_allowed_source_ips")] + pub allowed_source_ips: AllowedSourceIps, +} + +impl From for RackInitializeRequest { + fn from(v1: RackInitializeRequestV1) -> Self { + RackInitializeRequest { + trust_quorum_peers: v1.trust_quorum_peers, + bootstrap_discovery: v1.bootstrap_discovery, + ntp_servers: v1.ntp_servers, + dns_servers: v1.dns_servers, + internal_services_ip_pool_ranges: v1 + .internal_services_ip_pool_ranges, + external_dns_ips: v1.external_dns_ips, + external_dns_zone_name: v1.external_dns_zone_name, + external_certificates: v1.external_certificates, + recovery_silo: v1.recovery_silo, + rack_network_config: v1.rack_network_config.into(), + allowed_source_ips: v1.allowed_source_ips, + } + } +} + // "Shadow" copy of `RackInitializeRequest` that does no validation on its // fields. #[derive(Clone, Deserialize)] @@ -96,6 +153,22 @@ pub struct RackInitializeRequest { pub allowed_source_ips: AllowedSourceIps, } +impl RackInitializeRequest { + pub fn from_toml_with_fallback( + data: &str, + ) -> Result { + let v2_err = match toml::from_str::(&data) { + Ok(req) => return Ok(req), + Err(e) => e, + }; + if let Ok(v1) = toml::from_str::(&data) { + return Ok(v1.into()); + } + + Err(v2_err.into()) + } +} + /// This field was added after several racks were already deployed. RSS plans /// for those racks should default to allowing any source IP, since that is /// effectively what they did. @@ -141,29 +214,61 @@ impl std::fmt::Debug for RackInitializeRequest { } } +fn validate_external_dns( + dns_ips: &Vec, + internal_ranges: &Vec, +) -> Result<()> { + if dns_ips.is_empty() { + bail!("At least one external DNS IP is required"); + } + + // Every external DNS IP should also be present in one of the internal + // services IP pool ranges. This check is O(N*M), but we expect both N + // and M to be small (~5 DNS servers, and a small number of pools). + for &dns_ip in dns_ips { + if !internal_ranges.iter().any(|range| range.contains(dns_ip)) { + bail!( + "External DNS IP {dns_ip} is not contained in \ + `internal_services_ip_pool_ranges`" + ); + } + } + Ok(()) +} + +impl TryFrom for RackInitializeRequestV1 { + type Error = anyhow::Error; + + fn try_from(value: UnvalidatedRackInitializeRequestV1) -> Result { + validate_external_dns( + &value.external_dns_ips, + &value.internal_services_ip_pool_ranges, + )?; + + Ok(RackInitializeRequestV1 { + trust_quorum_peers: value.trust_quorum_peers, + bootstrap_discovery: value.bootstrap_discovery, + ntp_servers: value.ntp_servers, + dns_servers: value.dns_servers, + internal_services_ip_pool_ranges: value + .internal_services_ip_pool_ranges, + external_dns_ips: value.external_dns_ips, + external_dns_zone_name: value.external_dns_zone_name, + external_certificates: value.external_certificates, + recovery_silo: value.recovery_silo, + rack_network_config: value.rack_network_config, + allowed_source_ips: value.allowed_source_ips, + }) + } +} impl TryFrom for RackInitializeRequest { type Error = anyhow::Error; fn try_from(value: UnvalidatedRackInitializeRequest) -> Result { - if value.external_dns_ips.is_empty() { - bail!("At least one external DNS IP is required"); - } - - // Every external DNS IP should also be present in one of the internal - // services IP pool ranges. This check is O(N*M), but we expect both N - // and M to be small (~5 DNS servers, and a small number of pools). - for &dns_ip in &value.external_dns_ips { - if !value - .internal_services_ip_pool_ranges - .iter() - .any(|range| range.contains(dns_ip)) - { - bail!( - "External DNS IP {dns_ip} is not contained in \ - `internal_services_ip_pool_ranges`" - ); - } - } + validate_external_dns( + &value.external_dns_ips, + &value.internal_services_ip_pool_ranges, + )?; Ok(RackInitializeRequest { trust_quorum_peers: value.trust_quorum_peers, diff --git a/sled-agent/src/config.rs b/sled-agent/src/config.rs index c4ce421497..ac9b61f3bb 100644 --- a/sled-agent/src/config.rs +++ b/sled-agent/src/config.rs @@ -115,7 +115,7 @@ pub enum ConfigError { Parse { path: Utf8PathBuf, #[source] - err: toml::de::Error, + err: anyhow::Error, }, #[error("Loading certificate: {0}")] Certificate(#[source] anyhow::Error), @@ -130,8 +130,9 @@ impl Config { let path = path.as_ref(); let contents = std::fs::read_to_string(&path) .map_err(|err| ConfigError::Io { path: path.into(), err })?; - let config = toml::from_str(&contents) - .map_err(|err| ConfigError::Parse { path: path.into(), err })?; + let config = toml::from_str(&contents).map_err(|err| { + ConfigError::Parse { path: path.into(), err: err.into() } + })?; Ok(config) } diff --git a/sled-agent/src/rack_setup/config.rs b/sled-agent/src/rack_setup/config.rs index e52ed14304..01741f7284 100644 --- a/sled-agent/src/rack_setup/config.rs +++ b/sled-agent/src/rack_setup/config.rs @@ -12,14 +12,16 @@ use omicron_common::address::{ use crate::bootstrap::params::Certificate; pub use crate::bootstrap::params::RackInitializeRequest as SetupServiceConfig; +pub use crate::bootstrap::params::RackInitializeRequestV1 as SetupServiceConfigV1; impl SetupServiceConfig { pub fn from_file>(path: P) -> Result { let path = path.as_ref(); let contents = std::fs::read_to_string(&path) .map_err(|err| ConfigError::Io { path: path.into(), err })?; - let mut raw_config: SetupServiceConfig = toml::from_str(&contents) - .map_err(|err| ConfigError::Parse { path: path.into(), err })?; + let mut raw_config = + SetupServiceConfig::from_toml_with_fallback(&contents) + .map_err(|err| ConfigError::Parse { path: path.into(), err })?; // In the same way that sled-agent itself (our caller) discovers the // optional config-rss.toml in a well-known path relative to its config diff --git a/sled-agent/src/rack_setup/plan/sled.rs b/sled-agent/src/rack_setup/plan/sled.rs index a3fd57369a..c6d2e73ccd 100644 --- a/sled-agent/src/rack_setup/plan/sled.rs +++ b/sled-agent/src/rack_setup/plan/sled.rs @@ -9,6 +9,7 @@ use crate::bootstrap::{ config::BOOTSTRAP_AGENT_RACK_INIT_PORT, params::StartSledAgentRequest, }; use crate::rack_setup::config::SetupServiceConfig as Config; +use crate::rack_setup::config::SetupServiceConfigV1 as ConfigV1; use camino::Utf8PathBuf; use omicron_common::ledger::{self, Ledger, Ledgerable}; use schemars::JsonSchema; @@ -18,6 +19,7 @@ use sled_storage::manager::StorageHandle; use slog::Logger; use std::collections::{BTreeMap, BTreeSet}; use std::net::{Ipv6Addr, SocketAddrV6}; +use std::str::FromStr; use thiserror::Error; use uuid::Uuid; @@ -43,7 +45,7 @@ impl Ledgerable for Plan { } const RSS_SLED_PLAN_FILENAME: &str = "rss-sled-plan.json"; -#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] pub struct Plan { pub rack_id: Uuid, pub sleds: BTreeMap, @@ -53,6 +55,42 @@ pub struct Plan { pub config: Config, } +impl FromStr for Plan { + type Err = String; + + fn from_str(value: &str) -> Result { + #[derive(Deserialize)] + struct ShadowPlan { + pub rack_id: Uuid, + pub sleds: BTreeMap, + pub config: Config, + } + #[derive(Deserialize)] + struct ShadowPlanV1 { + pub rack_id: Uuid, + pub sleds: BTreeMap, + pub config: ConfigV1, + } + let v2_err = match serde_json::from_str::(&value) { + Ok(plan) => { + return Ok(Plan { + rack_id: plan.rack_id, + sleds: plan.sleds, + config: plan.config, + }) + } + Err(e) => format!("unable to parse Plan: {e:?}"), + }; + serde_json::from_str::(&value) + .map(|v1| Plan { + rack_id: v1.rack_id, + sleds: v1.sleds, + config: v1.config.into(), + }) + .map_err(|_| v2_err) + } +} + impl Plan { pub async fn load( log: &Logger, @@ -164,8 +202,8 @@ mod tests { let contents = std::fs::read_to_string(path.join(sled_plan_basename)) .expect("failed to read file"); - let parsed: Plan = - serde_json::from_str(&contents).expect("failed to parse file"); + let parsed = + Plan::from_str(&contents).expect("failed to parse file"); expectorate::assert_contents( out_path.join(sled_plan_basename), &serde_json::to_string_pretty(&parsed).unwrap(), diff --git a/sled-agent/tests/data/early_network_blobs.txt b/sled-agent/tests/data/early_network_blobs.txt index e9b9927e86..c968d4010b 100644 --- a/sled-agent/tests/data/early_network_blobs.txt +++ b/sled-agent/tests/data/early_network_blobs.txt @@ -1,2 +1,2 @@ -2023-11-30 mupdate failing blob,{"generation":15,"schema_version":1,"body":{"ntp_servers":[],"rack_network_config":{"rack_subnet":"fd00:1122:3344:100::/56","infra_ip_first":"0.0.0.0","infra_ip_last":"0.0.0.0","ports":[{"routes":[],"addresses":[],"switch":"switch1","port":"qsfp0","uplink_port_speed":"speed100_g","uplink_port_fec":"none","bgp_peers":[]},{"routes":[],"addresses":[{"address":"172.20.15.53/29"}],"switch":"switch1","port":"qsfp18","uplink_port_speed":"speed100_g","uplink_port_fec":"rs","bgp_peers":[{"asn":65002,"port":"qsfp18","addr":"172.20.15.51","hold_time":6,"idle_hold_time":6,"delay_open":0,"connect_retry":3,"keepalive":2}]},{"routes":[],"addresses":[{"address":"172.20.15.45/29"}],"switch":"switch0","port":"qsfp18","uplink_port_speed":"speed100_g","uplink_port_fec":"rs","bgp_peers":[{"asn":65002,"port":"qsfp18","addr":"172.20.15.43","hold_time":6,"idle_hold_time":6,"delay_open":0,"connect_retry":3,"keepalive":2}]},{"routes":[],"addresses":[],"switch":"switch0","port":"qsfp0","uplink_port_speed":"speed100_g","uplink_port_fec":"none","bgp_peers":[]}],"bgp":[{"asn":65002,"originate":["172.20.26.0/24"]},{"asn":65002,"originate":["172.20.26.0/24"]}]}}} -2023-12-06 config,{"generation":20,"schema_version":1,"body":{"ntp_servers":["ntp.example.com"],"rack_network_config":{"rack_subnet":"ff01::/32","infra_ip_first":"127.0.0.1","infra_ip_last":"127.1.0.1","ports":[{"routes":[{"destination":"10.1.9.32/16","nexthop":"10.1.9.32"}],"addresses":[{"address":"2001:db8::/96"}],"switch":"switch0","port":"foo","uplink_port_speed":"speed200_g","uplink_port_fec":"firecode","bgp_peers":[{"asn":65000,"port":"bar","addr":"1.2.3.4","hold_time":20,"idle_hold_time":50,"delay_open":null,"connect_retry":30,"keepalive":10}],"autoneg":true}],"bgp":[{"asn":20000,"originate":["192.168.0.0/24"]}]}}} +2023-11-30 mupdate failing blob,{"generation":15,"schema_version":1,"body":{"ntp_servers":[],"rack_network_config":{"rack_subnet":"fd00:1122:3344:100::/56","infra_ip_first":"0.0.0.0","infra_ip_last":"0.0.0.0","ports":[{"routes":[],"addresses":[],"switch":"switch1","port":"qsfp0","uplink_port_speed":"speed100_g","uplink_port_fec":"none","bgp_peers":[]},{"routes":[],"addresses":["172.20.15.53/29"],"switch":"switch1","port":"qsfp18","uplink_port_speed":"speed100_g","uplink_port_fec":"rs","bgp_peers":[{"asn":65002,"port":"qsfp18","addr":"172.20.15.51","hold_time":6,"idle_hold_time":6,"delay_open":0,"connect_retry":3,"keepalive":2}]},{"routes":[],"addresses":["172.20.15.45/29"],"switch":"switch0","port":"qsfp18","uplink_port_speed":"speed100_g","uplink_port_fec":"rs","bgp_peers":[{"asn":65002,"port":"qsfp18","addr":"172.20.15.43","hold_time":6,"idle_hold_time":6,"delay_open":0,"connect_retry":3,"keepalive":2}]},{"routes":[],"addresses":[],"switch":"switch0","port":"qsfp0","uplink_port_speed":"speed100_g","uplink_port_fec":"none","bgp_peers":[]}],"bgp":[{"asn":65002,"originate":["172.20.26.0/24"]},{"asn":65002,"originate":["172.20.26.0/24"]}]}}} +2023-12-06 config,{"generation":20,"schema_version":1,"body":{"ntp_servers":["ntp.example.com"],"rack_network_config":{"rack_subnet":"ff01::/32","infra_ip_first":"127.0.0.1","infra_ip_last":"127.1.0.1","ports":[{"routes":[{"destination":"10.1.9.32/16","nexthop":"10.1.9.32"}],"addresses":["2001:db8::/96"],"switch":"switch0","port":"foo","uplink_port_speed":"speed200_g","uplink_port_fec":"firecode","bgp_peers":[{"asn":65000,"port":"bar","addr":"1.2.3.4","hold_time":20,"idle_hold_time":50,"delay_open":null,"connect_retry":30,"keepalive":10}],"autoneg":true}],"bgp":[{"asn":20000,"originate":["192.168.0.0/24"]}]}}} diff --git a/sled-agent/tests/integration_tests/early_network.rs b/sled-agent/tests/integration_tests/early_network.rs index b7cab53a51..28fc0fd010 100644 --- a/sled-agent/tests/integration_tests/early_network.rs +++ b/sled-agent/tests/integration_tests/early_network.rs @@ -5,6 +5,7 @@ //! Tests that EarlyNetworkConfig deserializes across versions. use std::net::Ipv4Addr; +use std::str::FromStr; use bootstore::schemes::v0 as bootstore; use omicron_common::api::{ @@ -48,8 +49,8 @@ fn early_network_blobs_deserialize() { }); // Attempt to deserialize this blob. - let config = serde_json::from_str::(blob_json) - .unwrap_or_else(|error| { + let config = + EarlyNetworkConfig::from_str(blob_json).unwrap_or_else(|error| { panic!( "error deserializing early_network_blobs.txt \ \"{blob_desc}\" (line {blob_lineno}): {error}", @@ -113,7 +114,7 @@ fn current_config_example() -> (&'static str, EarlyNetworkConfig) { let description = "2023-12-06 config"; let config = EarlyNetworkConfig { generation: 20, - schema_version: 1, + schema_version: EarlyNetworkConfig::schema_version(), body: EarlyNetworkConfigBody { ntp_servers: vec!["ntp.example.com".to_owned()], rack_network_config: Some(RackNetworkConfig { diff --git a/sled-agent/tests/old-rss-sled-plans/madrid-rss-sled-plan.json b/sled-agent/tests/old-rss-sled-plans/madrid-rss-sled-plan.json index 683e8fb833..5512247ee8 100644 --- a/sled-agent/tests/old-rss-sled-plans/madrid-rss-sled-plan.json +++ b/sled-agent/tests/old-rss-sled-plans/madrid-rss-sled-plan.json @@ -1 +1 @@ -{"rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","sleds":{"[fdb0:a840:2504:396::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"b3e78a88-0f2e-476e-a8a9-2d8c90a169d6","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:103::/64"}}},"[fdb0:a840:2504:157::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"168e1ad6-1e4b-4f7a-b894-157974bd8bb8","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:104::/64"}}},"[fdb0:a840:2504:355::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"b9877212-212b-4588-b818-9c7b53c5b143","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:102::/64"}}},"[fdb0:a840:2504:3d2::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"c3a0f8be-5b05-4ee8-8c4e-2514de6501b6","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:101::/64"}}}},"config":{"rack_subnet":"fd00:1122:3344:100::","trust_quorum_peers":[{"type":"gimlet","identifier":"BRM42220081","model":"913-0000019","revision":6},{"type":"gimlet","identifier":"BRM42220046","model":"913-0000019","revision":6},{"type":"gimlet","identifier":"BRM44220001","model":"913-0000019","revision":6},{"type":"gimlet","identifier":"BRM42220004","model":"913-0000019","revision":6}],"bootstrap_discovery":{"type":"only_these","addrs":["fdb0:a840:2504:3d2::1","fdb0:a840:2504:355::1","fdb0:a840:2504:396::1","fdb0:a840:2504:157::1"]},"ntp_servers":["ntp.eng.oxide.computer"],"dns_servers":["1.1.1.1","9.9.9.9"],"internal_services_ip_pool_ranges":[{"first":"172.20.28.1","last":"172.20.28.10"}],"external_dns_ips":["172.20.28.1"],"external_dns_zone_name":"madrid.eng.oxide.computer","external_certificates":[{"cert":"","key":""}],"recovery_silo":{"silo_name":"recovery","user_name":"recovery","user_password_hash":"$argon2id$v=19$m=98304,t=13,p=1$RUlWc0ZxaHo0WFdrN0N6ZQ$S8p52j85GPvMhR/ek3GL0el/oProgTwWpHJZ8lsQQoY"},"rack_network_config":{"rack_subnet":"fd00:1122:3344:1::/56","infra_ip_first":"172.20.15.37","infra_ip_last":"172.20.15.38","ports":[{"routes":[{"destination":"0.0.0.0/0","nexthop":"172.20.15.33"}],"addresses":[{"address":"172.20.15.38/29"}],"switch":"switch0","port":"qsfp0","uplink_port_speed":"speed40_g","uplink_port_fec":"none","bgp_peers":[],"autoneg":false},{"routes":[{"destination":"0.0.0.0/0","nexthop":"172.20.15.33"}],"addresses":[{"address":"172.20.15.37/29"}],"switch":"switch1","port":"qsfp0","uplink_port_speed":"speed40_g","uplink_port_fec":"none","bgp_peers":[],"autoneg":false}],"bgp":[]}}} +{"rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","sleds":{"[fdb0:a840:2504:396::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"b3e78a88-0f2e-476e-a8a9-2d8c90a169d6","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:103::/64"}}},"[fdb0:a840:2504:157::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"168e1ad6-1e4b-4f7a-b894-157974bd8bb8","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:104::/64"}}},"[fdb0:a840:2504:355::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"b9877212-212b-4588-b818-9c7b53c5b143","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:102::/64"}}},"[fdb0:a840:2504:3d2::1]:12346":{"generation":0,"schema_version":1,"body":{"id":"c3a0f8be-5b05-4ee8-8c4e-2514de6501b6","rack_id":"ed6bcf59-9620-491d-8ebd-4a4eebf2e136","use_trust_quorum":true,"is_lrtq_learner":false,"subnet":{"net":"fd00:1122:3344:101::/64"}}}},"config":{"rack_subnet":"fd00:1122:3344:100::","trust_quorum_peers":[{"type":"gimlet","identifier":"BRM42220081","model":"913-0000019","revision":6},{"type":"gimlet","identifier":"BRM42220046","model":"913-0000019","revision":6},{"type":"gimlet","identifier":"BRM44220001","model":"913-0000019","revision":6},{"type":"gimlet","identifier":"BRM42220004","model":"913-0000019","revision":6}],"bootstrap_discovery":{"type":"only_these","addrs":["fdb0:a840:2504:3d2::1","fdb0:a840:2504:355::1","fdb0:a840:2504:396::1","fdb0:a840:2504:157::1"]},"ntp_servers":["ntp.eng.oxide.computer"],"dns_servers":["1.1.1.1","9.9.9.9"],"internal_services_ip_pool_ranges":[{"first":"172.20.28.1","last":"172.20.28.10"}],"external_dns_ips":["172.20.28.1"],"external_dns_zone_name":"madrid.eng.oxide.computer","external_certificates":[{"cert":"","key":""}],"recovery_silo":{"silo_name":"recovery","user_name":"recovery","user_password_hash":"$argon2id$v=19$m=98304,t=13,p=1$RUlWc0ZxaHo0WFdrN0N6ZQ$S8p52j85GPvMhR/ek3GL0el/oProgTwWpHJZ8lsQQoY"},"rack_network_config":{"rack_subnet":"fd00:1122:3344:1::/56","infra_ip_first":"172.20.15.37","infra_ip_last":"172.20.15.38","ports":[{"routes":[{"destination":"0.0.0.0/0","nexthop":"172.20.15.33"}],"addresses":["172.20.15.38/29"],"switch":"switch0","port":"qsfp0","uplink_port_speed":"speed40_g","uplink_port_fec":"none","bgp_peers":[],"autoneg":false},{"routes":[{"destination":"0.0.0.0/0","nexthop":"172.20.15.33"}],"addresses":["172.20.15.37/29"],"switch":"switch1","port":"qsfp0","uplink_port_speed":"speed40_g","uplink_port_fec":"none","bgp_peers":[],"autoneg":false}],"bgp":[]}}}