diff --git a/.envrc b/.envrc index 9731258f343..a6ab4f52174 100644 --- a/.envrc +++ b/.envrc @@ -5,6 +5,7 @@ PATH_add out/cockroachdb/bin PATH_add out/clickhouse PATH_add out/dendrite-stub/bin PATH_add out/mgd/root/opt/oxide/mgd/bin +PATH_add out/mg-ddm/root/opt/oxide/mg-ddm/bin if [ "$OMICRON_USE_FLAKE" = 1 ] && nix flake show &> /dev/null then diff --git a/Cargo.lock b/Cargo.lock index 398809f5032..f42ac8357a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -102,7 +102,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", - "anstyle-parse", + "anstyle-parse 0.2.7", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse 1.0.0", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -112,9 +127,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" @@ -125,6 +140,15 @@ dependencies = [ "utf8parse", ] +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + [[package]] name = "anstyle-query" version = "1.1.5" @@ -161,6 +185,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "api_identity" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "omicron-workspace-hack", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "approx" version = "0.5.1" @@ -620,12 +655,31 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "bhyve_api" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" +dependencies = [ + "bhyve_api_sys 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", + "libc", + "strum 0.26.3", +] + [[package]] name = "bhyve_api" version = "0.0.0" source = "git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845#bc489ddf0f38f75e0c194b86cf6f0de377f68845" dependencies = [ - "bhyve_api_sys", + "bhyve_api_sys 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845)", + "libc", + "strum 0.26.3", +] + +[[package]] +name = "bhyve_api_sys" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" +dependencies = [ "libc", "strum 0.26.3", ] @@ -824,7 +878,7 @@ dependencies = [ "derive_more 0.99.20", "hex", "hkdf", - "omicron-ledger", + "omicron-ledger 0.1.0", "omicron-workspace-hack", "proptest", "rand 0.8.6", @@ -832,7 +886,7 @@ dependencies = [ "serde", "serde_with", "sha3", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "slog-async", "slog-error-chain", @@ -844,20 +898,49 @@ dependencies = [ "zeroize", ] +[[package]] +name = "bootstore" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "bytes", + "camino", + "chacha20poly1305", + "ciborium", + "derive_more 0.99.20", + "hex", + "hkdf", + "omicron-ledger 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "omicron-workspace-hack", + "rand 0.8.6", + "secrecy 0.10.3", + "serde", + "serde_with", + "sha3", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "slog", + "slog-error-chain", + "thiserror 2.0.18", + "tokio", + "uuid", + "vsss-rs", + "zeroize", +] + [[package]] name = "bootstrap-agent-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "schemars 0.8.22", "serde", - "sled-agent-types", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", "tufaceous-artifact", ] @@ -875,8 +958,8 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "uuid", ] @@ -885,8 +968,8 @@ dependencies = [ name = "bootstrap-agent-lockstep-api" version = "0.1.0" dependencies = [ - "bootstrap-agent-lockstep-types", - "dropshot 0.17.0", + "bootstrap-agent-lockstep-types 0.1.0", + "dropshot", "omicron-uuid-kinds", "omicron-workspace-hack", ] @@ -905,8 +988,8 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", ] @@ -916,13 +999,30 @@ version = "0.1.0" dependencies = [ "anyhow", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", + "strum 0.27.2", +] + +[[package]] +name = "bootstrap-agent-lockstep-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "omicron-common", + "omicron-passwords 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-uuid-kinds", "omicron-workspace-hack", "schemars 0.8.22", "serde", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "strum 0.27.2", ] @@ -1041,7 +1141,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8c236e531d024b1524669ee2a56eca09ab8a40f3395dc2728cde9defa9c60d8" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "camino", "camino-tempfile", @@ -1064,15 +1164,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - [[package]] name = "cargo-platform" version = "0.2.0" @@ -1108,20 +1199,6 @@ dependencies = [ "url", ] -[[package]] -name = "cargo_metadata" -version = "0.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" -dependencies = [ - "camino", - "cargo-platform 0.1.9", - "semver 1.0.28", - "serde", - "serde_json", - "thiserror 2.0.18", -] - [[package]] name = "cargo_metadata" version = "0.21.0" @@ -1233,7 +1310,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "nom", + "nom 7.1.3", ] [[package]] @@ -1270,7 +1347,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "dropshot 0.17.0", + "dropshot", "futures", "libc", "omicron-common", @@ -1402,9 +1479,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.60" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -1412,11 +1489,11 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.60" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "anstream", + "anstream 1.0.0", "anstyle", "clap_lex", "strsim", @@ -1425,9 +1502,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -1450,8 +1527,8 @@ dependencies = [ "chrono", "clap", "clickhouse-admin-server-client", - "clickhouse-admin-types", - "dropshot 0.17.0", + "clickhouse-admin-types 0.1.0", + "dropshot", "futures", "omicron-common", "omicron-workspace-hack", @@ -1472,8 +1549,8 @@ dependencies = [ name = "clickhouse-admin-api" version = "0.1.0" dependencies = [ - "clickhouse-admin-types-versions", - "dropshot 0.17.0", + "clickhouse-admin-types-versions 0.1.0", + "dropshot", "dropshot-api-manager-types", "omicron-common", "omicron-uuid-kinds", @@ -1488,7 +1565,7 @@ name = "clickhouse-admin-keeper-client" version = "0.1.0" dependencies = [ "chrono", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "omicron-uuid-kinds", "omicron-workspace-hack", "progenitor 0.14.0", @@ -1503,7 +1580,7 @@ name = "clickhouse-admin-server-client" version = "0.1.0" dependencies = [ "chrono", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "omicron-uuid-kinds", "omicron-workspace-hack", "progenitor 0.14.0", @@ -1518,7 +1595,7 @@ name = "clickhouse-admin-single-client" version = "0.1.0" dependencies = [ "chrono", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "omicron-uuid-kinds", "omicron-workspace-hack", "progenitor 0.14.0", @@ -1533,9 +1610,9 @@ name = "clickhouse-admin-test-utils" version = "0.1.0" dependencies = [ "camino", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "clickward", - "dropshot 0.17.0", + "dropshot", "omicron-workspace-hack", ] @@ -1543,7 +1620,16 @@ dependencies = [ name = "clickhouse-admin-types" version = "0.1.0" dependencies = [ - "clickhouse-admin-types-versions", + "clickhouse-admin-types-versions 0.1.0", + "omicron-workspace-hack", +] + +[[package]] +name = "clickhouse-admin-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "clickhouse-admin-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-workspace-hack", ] @@ -1571,6 +1657,29 @@ dependencies = [ "slog-term", ] +[[package]] +name = "clickhouse-admin-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "atomicwrites", + "camino", + "camino-tempfile", + "chrono", + "daft", + "derive_more 0.99.20", + "expectorate", + "iddqd", + "itertools 0.14.0", + "omicron-common", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "serde_json", + "slog", +] + [[package]] name = "clickhouse-cluster-dev" version = "0.1.0" @@ -1580,7 +1689,7 @@ dependencies = [ "clickward", "omicron-workspace-hack", "oxide-tokio-rt", - "oximeter-db", + "oximeter-db 0.1.0", "oximeter-test-utils", "scopeguard", "slog", @@ -1634,8 +1743,8 @@ dependencies = [ name = "cockroach-admin-api" version = "0.1.0" dependencies = [ - "cockroach-admin-types-versions", - "dropshot 0.17.0", + "cockroach-admin-types-versions 0.1.0", + "dropshot", "dropshot-api-manager-types", "http", "omicron-common", @@ -1661,7 +1770,17 @@ dependencies = [ name = "cockroach-admin-types" version = "0.1.0" dependencies = [ - "cockroach-admin-types-versions", + "cockroach-admin-types-versions 0.1.0", + "omicron-workspace-hack", + "serde", +] + +[[package]] +name = "cockroach-admin-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "cockroach-admin-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-workspace-hack", "serde", ] @@ -1682,6 +1801,20 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "cockroach-admin-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "chrono", + "csv", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "thiserror 2.0.18", +] + [[package]] name = "colorchoice" version = "1.0.4" @@ -1720,7 +1853,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dendrite?branch=main#f20f786e67c86388dfaaf0ef3aa9d2693dfe1da4" +source = "git+https://github.com/oxidecomputer/dendrite?branch=main#50ed531201ea5f85115141a2203d551fbbdaa3da" dependencies = [ "anyhow", "chrono", @@ -1733,7 +1866,7 @@ dependencies = [ "slog-async", "slog-bunyan", "slog-term", - "smf 0.10.0", + "smf 0.10.0 (git+https://github.com/illumos/smf-rs)", "thiserror 1.0.69", "tokio", ] @@ -1741,7 +1874,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da#50ed531201ea5f85115141a2203d551fbbdaa3da" +source = "git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be#90efdc6cf618523320bc403f1b9484dbd7f762be" dependencies = [ "anyhow", "chrono", @@ -1754,7 +1887,7 @@ dependencies = [ "slog-async", "slog-bunyan", "slog-term", - "smf 0.10.0", + "smf 0.10.0 (git+https://github.com/illumos/smf-rs)", "thiserror 1.0.69", "tokio", ] @@ -1776,7 +1909,7 @@ dependencies = [ "slog-async", "slog-bunyan", "slog-term", - "smf 0.10.0", + "smf 0.10.0 (git+https://github.com/illumos/smf-rs)", "thiserror 1.0.69", "tokio", ] @@ -2020,7 +2153,7 @@ name = "crdb-seed" version = "0.1.0" dependencies = [ "anyhow", - "dropshot 0.17.0", + "dropshot", "omicron-test-utils", "omicron-workspace-hack", "slog", @@ -2150,14 +2283,14 @@ dependencies = [ [[package]] name = "crucible-agent-client" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=7103cd3a3d7b0112d2949dd135db06fef0c156bb#7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "anyhow", "chrono", "crucible-workspace-hack", "percent-encoding", - "progenitor 0.10.0", - "reqwest 0.12.28", + "progenitor 0.13.0", + "reqwest 0.13.2", "schemars 0.8.22", "serde", "serde_json", @@ -2176,16 +2309,29 @@ dependencies = [ "uuid", ] +[[package]] +name = "crucible-client-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/crucible?rev=bd9a0e2abe6b6b89aec8c85f4ee57474144ed150#bd9a0e2abe6b6b89aec8c85f4ee57474144ed150" +dependencies = [ + "base64 0.22.1", + "crucible-workspace-hack", + "schemars 0.8.22", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "crucible-common" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=7103cd3a3d7b0112d2949dd135db06fef0c156bb#7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "anyhow", "atty", "crucible-workspace-hack", - "dropshot 0.16.7", - "nix 0.29.0", + "dropshot", + "nix 0.31.2", "rustls-pemfile 1.0.4", "schemars 0.8.22", "serde", @@ -2199,7 +2345,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-rustls 0.24.1", - "toml 0.8.23", + "toml 1.0.6+spec-1.1.0", "twox-hash", "uuid", "vergen", @@ -2209,20 +2355,32 @@ dependencies = [ [[package]] name = "crucible-pantry-client" version = "0.0.1" -source = "git+https://github.com/oxidecomputer/crucible?rev=7103cd3a3d7b0112d2949dd135db06fef0c156bb#7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" dependencies = [ "anyhow", "chrono", "crucible-workspace-hack", "percent-encoding", - "progenitor 0.10.0", - "reqwest 0.12.28", + "progenitor 0.13.0", + "reqwest 0.13.2", "schemars 0.8.22", "serde", "serde_json", "uuid", ] +[[package]] +name = "crucible-smf" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431#3c1708d86e10f0370807388a1efe092edd99d431" +dependencies = [ + "crucible-workspace-hack", + "libc", + "num-derive 0.4.2", + "num-traits", + "thiserror 2.0.18", +] + [[package]] name = "crucible-smf" version = "0.0.0" @@ -2525,16 +2683,40 @@ dependencies = [ [[package]] name = "ddm-admin-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/maghemite?rev=7696ee48d5ee29a917dea459e281fe2e8ff20513#7696ee48d5ee29a917dea459e281fe2e8ff20513" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" dependencies = [ + "ddm-api-types-versions", "oxnet", - "progenitor 0.13.0", + "progenitor 0.14.0", "reqwest 0.13.2", "serde", "slog", "uuid", ] +[[package]] +name = "ddm-api-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "ddm-api-types-versions", +] + +[[package]] +name = "ddm-api-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "multicast-types", + "omicron-common", + "oxnet", + "schemars 0.8.22", + "serde", + "serde_repr", + "thiserror 2.0.18", + "uuid", +] + [[package]] name = "debug-ignore" version = "1.0.5" @@ -2937,11 +3119,11 @@ dependencies = [ [[package]] name = "dlpi" version = "0.2.0" -source = "git+https://github.com/oxidecomputer/dlpi-sys#d9645f8d61187e76384474b1100f6537fb644993" +source = "git+https://github.com/oxidecomputer/dlpi-sys#a611f32a2374579d25e628b395a9cb251f753388" dependencies = [ "libc", "libdlpi-sys", - "num_enum 0.7.5", + "num_enum 0.7.6", "pretty-hex", "thiserror 2.0.18", ] @@ -2957,14 +3139,14 @@ dependencies = [ "clap", "dns-server-api", "dns-service-client", - "dropshot 0.17.0", + "dropshot", "git-stub-vcs", "hickory-client", "hickory-proto 0.25.2", "hickory-resolver 0.25.2", "hickory-server", - "internal-dns-types", - "internal-dns-types-versions", + "internal-dns-types 0.1.0", + "internal-dns-types-versions 0.1.0", "omicron-common", "omicron-test-utils", "omicron-workspace-hack", @@ -2989,9 +3171,9 @@ name = "dns-server-api" version = "0.1.0" dependencies = [ "chrono", - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", - "internal-dns-types-versions", + "internal-dns-types-versions 0.1.0", "omicron-workspace-hack", "schemars 0.8.22", "semver 1.0.28", @@ -3005,7 +3187,7 @@ dependencies = [ "chrono", "expectorate", "http", - "internal-dns-types-versions", + "internal-dns-types-versions 0.1.0", "omicron-workspace-hack", "progenitor 0.14.0", "reqwest 0.13.2", @@ -3024,9 +3206,9 @@ dependencies = [ "clap", "dns-server", "dns-service-client", - "dropshot 0.17.0", + "dropshot", "expectorate", - "internal-dns-types", + "internal-dns-types 0.1.0", "omicron-test-utils", "omicron-workspace-hack", "oxide-tokio-rt", @@ -3084,7 +3266,7 @@ checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" [[package]] name = "dpd-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dendrite?branch=main#f20f786e67c86388dfaaf0ef3aa9d2693dfe1da4" +source = "git+https://github.com/oxidecomputer/dendrite?branch=main#50ed531201ea5f85115141a2203d551fbbdaa3da" dependencies = [ "async-trait", "chrono", @@ -3108,11 +3290,11 @@ dependencies = [ [[package]] name = "dpd-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da#50ed531201ea5f85115141a2203d551fbbdaa3da" +source = "git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be#90efdc6cf618523320bc403f1b9484dbd7f762be" dependencies = [ "async-trait", "chrono", - "common 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da)", + "common 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be)", "crc8", "futures", "http", @@ -3167,57 +3349,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "dropshot" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69fd85c8dfc67252d02f260595f6b62b5abceb1b88b4b9722369d27936e5fa4" -dependencies = [ - "async-stream", - "async-trait", - "base64 0.22.1", - "bytes", - "camino", - "chrono", - "debug-ignore", - "dropshot_endpoint 0.16.7", - "form_urlencoded", - "futures", - "hostname 0.4.2", - "http", - "http-body-util", - "hyper", - "hyper-util", - "indexmap 2.14.0", - "multer", - "openapiv3", - "paste", - "percent-encoding", - "rustls 0.22.4", - "rustls-pemfile 2.2.0", - "schemars 0.8.22", - "scopeguard", - "semver 1.0.28", - "serde", - "serde_json", - "serde_path_to_error", - "serde_urlencoded", - "sha1", - "slog", - "slog-async", - "slog-bunyan", - "slog-json", - "slog-term", - "thiserror 2.0.18", - "tokio", - "tokio-rustls 0.25.0", - "toml 0.9.12+spec-1.1.0", - "usdt 0.6.0", - "uuid", - "version_check", - "waitgroup", -] - [[package]] name = "dropshot" version = "0.17.0" @@ -3232,7 +3363,7 @@ dependencies = [ "camino", "chrono", "debug-ignore", - "dropshot_endpoint 0.17.0", + "dropshot_endpoint", "form_urlencoded", "futures", "hostname 0.4.2", @@ -3283,7 +3414,7 @@ dependencies = [ "clap", "debug-ignore", "drift", - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "fs-err 3.3.0", "git-stub", @@ -3317,21 +3448,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "dropshot_endpoint" -version = "0.16.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67d106478e4a4782556981d028a667f41c4845cdaa6e2d3a9f58c5d15e725401" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "semver 1.0.28", - "serde", - "serde_tokenstream", - "syn 2.0.117", -] - [[package]] name = "dropshot_endpoint" version = "0.17.0" @@ -3506,7 +3622,7 @@ dependencies = [ "anyhow", "async-trait", "base64 0.22.1", - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "bytes", "chrono", "clap", @@ -3517,8 +3633,8 @@ dependencies = [ "hickory-resolver 0.25.2", "http", "humantime", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "internet-checksum", "ispf", "macaddr", @@ -3536,7 +3652,7 @@ dependencies = [ "russh-keys", "serde", "serde_json", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "socket2 0.5.10", @@ -3579,7 +3695,7 @@ version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2daee4ea451f429a58296525ddf28b45a3b64f1acf6587e2067437bb11e218d" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "env_filter", "log", @@ -3615,7 +3731,21 @@ dependencies = [ name = "ereport-types" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "ereport-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "dropshot", "omicron-uuid-kinds", "omicron-workspace-hack", "schemars 0.8.22", @@ -4048,10 +4178,10 @@ dependencies = [ name = "gateway-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", - "ereport-types", - "gateway-types-versions", + "ereport-types 0.1.0", + "gateway-types-versions 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -4068,7 +4198,7 @@ dependencies = [ "anyhow", "clap", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "omicron-common", "omicron-uuid-kinds", @@ -4095,9 +4225,34 @@ dependencies = [ "base64 0.22.1", "chrono", "daft", - "ereport-types", + "ereport-types 0.1.0", "gateway-messages", - "gateway-types", + "gateway-types 0.1.0", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "progenitor 0.14.0", + "rand 0.9.2", + "reqwest 0.13.2", + "schemars 0.8.22", + "serde", + "serde_json", + "slog", + "thiserror 2.0.18", + "tokio", + "uuid", +] + +[[package]] +name = "gateway-client" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "base64 0.22.1", + "chrono", + "daft", + "ereport-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "gateway-messages", + "gateway-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-uuid-kinds", "omicron-workspace-hack", "progenitor 0.14.0", @@ -4183,10 +4338,10 @@ name = "gateway-test-utils" version = "0.1.0" dependencies = [ "camino", - "dropshot 0.17.0", - "gateway-client", + "dropshot", + "gateway-client 0.1.0", "gateway-messages", - "gateway-types", + "gateway-types 0.1.0", "omicron-gateway", "omicron-test-utils", "omicron-workspace-hack", @@ -4202,7 +4357,16 @@ dependencies = [ name = "gateway-types" version = "0.1.0" dependencies = [ - "gateway-types-versions", + "gateway-types-versions 0.1.0", + "omicron-workspace-hack", +] + +[[package]] +name = "gateway-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "gateway-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-workspace-hack", ] @@ -4211,7 +4375,7 @@ name = "gateway-types-versions" version = "0.1.0" dependencies = [ "daft", - "dropshot 0.17.0", + "dropshot", "gateway-messages", "hex", "omicron-uuid-kinds", @@ -4225,6 +4389,24 @@ dependencies = [ "uuid", ] +[[package]] +name = "gateway-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "daft", + "dropshot", + "gateway-messages", + "hex", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "thiserror 2.0.18", + "tufaceous-artifact", + "uuid", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4293,13 +4475,29 @@ version = "0.1.0" dependencies = [ "digest", "omicron-workspace-hack", - "proptest", + "proptest", + "rand 0.9.2", + "schemars 0.8.22", + "secrecy 0.10.3", + "serde", + "subtle", + "test-strategy", + "thiserror 2.0.18", + "zeroize", +] + +[[package]] +name = "gfss" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "digest", + "omicron-workspace-hack", "rand 0.9.2", "schemars 0.8.22", "secrecy 0.10.3", "serde", "subtle", - "test-strategy", "thiserror 2.0.18", "zeroize", ] @@ -5333,21 +5531,21 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "bhyve_api", + "bhyve_api 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", "byteorder", "camino", "camino-tempfile", "cfg-if", "chrono", - "crucible-smf", + "crucible-smf 0.0.0 (git+https://github.com/oxidecomputer/crucible?rev=3c1708d86e10f0370807388a1efe092edd99d431)", "debug-ignore", - "dropshot 0.17.0", + "dropshot", "futures", "http", "iddqd", "ipnetwork", "itertools 0.14.0", - "key-manager-types", + "key-manager-types 0.1.0", "libc", "macaddr", "nix 0.31.2", @@ -5364,7 +5562,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-async", "slog-error-chain", @@ -5380,6 +5578,55 @@ dependencies = [ "zone", ] +[[package]] +name = "illumos-utils" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "async-trait", + "bhyve_api 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845)", + "byteorder", + "camino", + "camino-tempfile", + "cfg-if", + "chrono", + "crucible-smf 0.0.0 (git+https://github.com/oxidecomputer/crucible?rev=7103cd3a3d7b0112d2949dd135db06fef0c156bb)", + "debug-ignore", + "dropshot", + "futures", + "http", + "iddqd", + "ipnetwork", + "itertools 0.14.0", + "key-manager-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "libc", + "macaddr", + "nix 0.31.2", + "omicron-common", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "opte-ioctl", + "oxide-vpc", + "oxlog", + "oxnet", + "rustix 1.1.3", + "schemars 0.8.22", + "serde", + "sled-agent-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "slog", + "slog-async", + "slog-error-chain", + "slog-term", + "smf 0.2.3", + "thiserror 2.0.18", + "tofino", + "tokio", + "uuid", + "whoami 1.6.1", + "zone", +] + [[package]] name = "impl-trait-for-tuples" version = "0.2.3" @@ -5530,7 +5777,7 @@ dependencies = [ "hex-literal", "http", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "installinator-client", "installinator-common", "ipcc", @@ -5548,7 +5795,7 @@ dependencies = [ "serde_json", "sha2", "sled-hardware", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "sled-storage", "slog", "slog-async", @@ -5562,7 +5809,7 @@ dependencies = [ "tokio-stream", "tufaceous-artifact", "tufaceous-lib", - "update-engine", + "update-engine 0.1.0", ] [[package]] @@ -5570,7 +5817,7 @@ name = "installinator-api" version = "0.1.0" dependencies = [ "anyhow", - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "hyper", "installinator-common-versions", @@ -5581,7 +5828,7 @@ dependencies = [ "serde", "slog", "tufaceous-artifact", - "update-engine", + "update-engine 0.1.0", "uuid", ] @@ -5599,7 +5846,7 @@ dependencies = [ "serde", "serde_json", "slog", - "update-engine", + "update-engine 0.1.0", ] [[package]] @@ -5608,7 +5855,7 @@ version = "0.1.0" dependencies = [ "anyhow", "camino", - "illumos-utils", + "illumos-utils 0.1.0", "installinator-common-versions", "libc", "omicron-common", @@ -5621,7 +5868,7 @@ dependencies = [ "test-strategy", "thiserror 2.0.18", "tokio", - "update-engine", + "update-engine 0.1.0", ] [[package]] @@ -5649,10 +5896,10 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "dropshot 0.17.0", + "dropshot", "hickory-resolver 0.25.2", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "omicron-common", "omicron-workspace-hack", "oxide-tokio-rt", @@ -5669,12 +5916,12 @@ dependencies = [ "dns-server", "dns-server-api", "dns-service-client", - "dropshot 0.17.0", + "dropshot", "expectorate", "futures", "hickory-proto 0.25.2", "hickory-resolver 0.25.2", - "internal-dns-types", + "internal-dns-types 0.1.0", "omicron-common", "omicron-test-utils", "omicron-uuid-kinds", @@ -5692,6 +5939,24 @@ dependencies = [ "tokio", ] +[[package]] +name = "internal-dns-resolver" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "futures", + "hickory-proto 0.25.2", + "hickory-resolver 0.25.2", + "internal-dns-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "omicron-common", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "qorb", + "reqwest 0.13.2", + "slog", + "thiserror 2.0.18", +] + [[package]] name = "internal-dns-types" version = "0.1.0" @@ -5699,7 +5964,7 @@ dependencies = [ "anyhow", "chrono", "expectorate", - "internal-dns-types-versions", + "internal-dns-types-versions 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -5709,9 +5974,38 @@ dependencies = [ "strum 0.27.2", ] +[[package]] +name = "internal-dns-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "chrono", + "internal-dns-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "omicron-common", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "strum 0.27.2", +] + +[[package]] +name = "internal-dns-types-versions" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "omicron-common", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", +] + [[package]] name = "internal-dns-types-versions" version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "anyhow", "chrono", @@ -5956,7 +6250,7 @@ version = "0.1.0" dependencies = [ "async-trait", "hkdf", - "key-manager-types", + "key-manager-types 0.1.0", "omicron-common", "omicron-workspace-hack", "secrecy 0.10.3", @@ -5975,6 +6269,15 @@ dependencies = [ "secrecy 0.10.3", ] +[[package]] +name = "key-manager-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "omicron-workspace-hack", + "secrecy 0.10.3", +] + [[package]] name = "knuffel" version = "3.2.0" @@ -6079,7 +6382,7 @@ checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libdlpi-sys" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/dlpi-sys#d9645f8d61187e76384474b1100f6537fb644993" +source = "git+https://github.com/oxidecomputer/dlpi-sys#a611f32a2374579d25e628b395a9cb251f753388" [[package]] name = "libefi-illumos" @@ -6159,23 +6462,23 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libnet" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/netadm-sys?branch=main#1e40efd8497973ef3b7d6f0285510424f53f43c5" +source = "git+https://github.com/oxidecomputer/netadm-sys?branch=main#ca2cb23e186d8f75dd1f206a7bbe9699758ab12d" dependencies = [ "anyhow", "cfg-if", "colored 3.1.1", "dlpi", "libc", - "num_enum 0.7.5", + "num_enum 0.7.6", "nvpair", "nvpair-sys", "oxnet", - "rand 0.10.0", + "rand 0.10.1", "rusty-doors", "socket2 0.6.3", "thiserror 2.0.18", "tracing", - "winnow 0.7.14", + "winnow 1.0.3", ] [[package]] @@ -6243,7 +6546,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5297962ef19edda4ce33aaa484386e0a5b3d7f2f4e037cbeee00503ef6b29d33" dependencies = [ - "anstream", + "anstream 0.6.21", "anstyle", "clap", "escape8259", @@ -6340,14 +6643,15 @@ dependencies = [ [[package]] name = "lldpd-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/lldp#d22509dfdb051321b859e924948605115691b93c" +source = "git+https://github.com/oxidecomputer/lldp#0d199cebf8fe3b24b9d943bdc46a00c227e5a345" dependencies = [ "chrono", "futures", "lldpd-common", - "progenitor 0.11.2", + "lldpd-types-versions", + "progenitor 0.13.0", "protocol", - "reqwest 0.12.28", + "reqwest 0.13.2", "schemars 0.8.22", "serde", "serde_json", @@ -6358,7 +6662,7 @@ dependencies = [ [[package]] name = "lldpd-common" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/lldp#d22509dfdb051321b859e924948605115691b93c" +source = "git+https://github.com/oxidecomputer/lldp#0d199cebf8fe3b24b9d943bdc46a00c227e5a345" dependencies = [ "anyhow", "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?branch=main)", @@ -6372,6 +6676,18 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "lldpd-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/lldp#0d199cebf8fe3b24b9d943bdc46a00c227e5a345" +dependencies = [ + "chrono", + "protocol", + "schemars 0.8.22", + "serde", + "uuid", +] + [[package]] name = "lock_api" version = "0.4.14" @@ -6539,6 +6855,25 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mg-admin-client" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "chrono", + "colored 3.1.1", + "mg-api-types-versions", + "mg-common", + "progenitor 0.14.0", + "reqwest 0.13.2", + "schemars 0.8.22", + "serde", + "serde_json", + "slog", + "tabwriter", + "uuid", +] + [[package]] name = "mg-admin-client" version = "0.1.0" @@ -6557,6 +6892,58 @@ dependencies = [ "uuid", ] +[[package]] +name = "mg-api-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "mg-api-types-versions", +] + +[[package]] +name = "mg-api-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "chrono", + "multicast-types", + "nom 8.0.0", + "num_enum 0.7.6", + "omicron-common", + "oxnet", + "schemars 0.8.22", + "serde", + "serde_json", + "slog", + "thiserror 2.0.18", + "uuid", +] + +[[package]] +name = "mg-common" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "anstyle", + "anyhow", + "backoff", + "clap", + "ddm-api-types", + "libc", + "libnet", + "omicron-common", + "oximeter 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "oximeter-producer 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "oxnet", + "schemars 0.8.22", + "serde", + "slog", + "slog-async", + "slog-bunyan", + "smf 0.10.0 (git+https://github.com/illumos/smf-rs?branch=main)", + "uuid", +] + [[package]] name = "mgs-dev" version = "0.1.0" @@ -6565,7 +6952,7 @@ dependencies = [ "camino", "clap", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-test-utils", "libc", @@ -6686,6 +7073,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "multicast-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8#5060d5864df864741dac8a0a4078fa4ecc3c73c8" +dependencies = [ + "omicron-common", + "schemars 0.8.22", + "serde", + "thiserror 2.0.18", +] + [[package]] name = "multimap" version = "0.10.1" @@ -6788,7 +7186,7 @@ dependencies = [ "base64 0.22.1", "chrono", "cookie", - "dropshot 0.17.0", + "dropshot", "futures", "headers", "http", @@ -6796,7 +7194,7 @@ dependencies = [ "newtype_derive", "nexus-db-fixed-data", "nexus-db-model", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-test-utils", @@ -6832,7 +7230,32 @@ dependencies = [ "chrono", "futures", "iddqd", - "nexus-types", + "nexus-types 0.1.0", + "omicron-common", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "oxnet", + "progenitor 0.14.0", + "regress 0.10.5", + "reqwest 0.13.2", + "schemars 0.8.22", + "serde", + "serde_json", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "slog", + "uuid", +] + +[[package]] +name = "nexus-client" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "chrono", + "futures", + "iddqd", + "nexus-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -6843,8 +7266,8 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types", - "sled-agent-types-versions", + "sled-agent-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-agent-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "slog", "uuid", ] @@ -6855,11 +7278,11 @@ version = "0.1.0" dependencies = [ "anyhow", "camino", - "dropshot 0.17.0", + "dropshot", "expectorate", "ipnet", "libc", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -6868,7 +7291,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "sled-agent-types", + "sled-agent-types 0.1.0", "tokio-postgres", "toml 0.8.23", "uuid", @@ -6890,7 +7313,7 @@ name = "nexus-db-fixed-data" version = "0.1.0" dependencies = [ "nexus-db-model", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-uuid-kinds", @@ -6915,7 +7338,7 @@ dependencies = [ "nexus-db-model", "nexus-db-queries", "nexus-db-schema", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-test-utils", @@ -6936,16 +7359,16 @@ dependencies = [ "camino", "camino-tempfile", "chrono", - "clickhouse-admin-types", - "cockroach-admin-types", + "clickhouse-admin-types 0.1.0", + "cockroach-admin-types 0.1.0", "db-macros", "derive-where", "diesel", - "ereport-types", + "ereport-types 0.1.0", "expectorate", "hex", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "ipnetwork", "itertools 0.14.0", "macaddr", @@ -6953,11 +7376,11 @@ dependencies = [ "nexus-config", "nexus-db-schema", "nexus-defaults", - "nexus-types", + "nexus-types 0.1.0", "omicron-certificates", "omicron-cockroach-metrics", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-rpaths", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -6973,8 +7396,8 @@ dependencies = [ "serde", "serde_json", "sled-agent-client", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "sqlparser", @@ -6983,7 +7406,7 @@ dependencies = [ "test-strategy", "thiserror 2.0.18", "tokio", - "trust-quorum-protocol", + "trust-quorum-protocol 0.1.0", "tufaceous-artifact", "uuid", "vergen-gitcl", @@ -7000,26 +7423,26 @@ dependencies = [ "camino", "camino-tempfile", "chrono", - "clickhouse-admin-types", - "cockroach-admin-types", + "clickhouse-admin-types 0.1.0", + "cockroach-admin-types 0.1.0", "const_format", "criterion", "db-macros", "diesel", "diesel-dtrace", - "dropshot 0.17.0", - "ereport-types", + "dropshot", + "ereport-types 0.1.0", "expectorate", "fmd-adm-sys", "futures", - "gateway-client", - "gateway-types", + "gateway-client 0.1.0", + "gateway-types 0.1.0", "hex", "hyper-rustls", "iddqd", - "illumos-utils", - "internal-dns-resolver", - "internal-dns-types", + "illumos-utils 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnetwork", "itertools 0.14.0", "macaddr", @@ -7033,11 +7456,11 @@ dependencies = [ "nexus-inventory", "nexus-reconfigurator-planning", "nexus-test-utils", - "nexus-types", + "nexus-types 0.1.0", "nonempty", "omicron-cockroach-metrics", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-rpaths", "omicron-sled-agent", "omicron-test-utils", @@ -7068,8 +7491,8 @@ dependencies = [ "serde_with", "sha2", "sled-agent-client", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "static_assertions", @@ -7080,8 +7503,8 @@ dependencies = [ "term 0.7.0", "thiserror 2.0.18", "tokio", - "trust-quorum-protocol", - "trust-quorum-types", + "trust-quorum-protocol 0.1.0", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "url", "usdt 0.5.0", @@ -7122,30 +7545,30 @@ name = "nexus-external-api" version = "0.1.0" dependencies = [ "anyhow", - "api_identity", + "api_identity 0.1.0", "base64 0.22.1", "chrono", - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "http", "hyper", "ipnetwork", "itertools 0.14.0", - "nexus-types", - "nexus-types-versions", + "nexus-types 0.1.0", + "nexus-types-versions 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "openapiv3", "oximeter-types-versions 0.1.0", "oxnet", - "oxql-types", + "oxql-types 0.1.0", "schemars 0.8.22", "scim2-rs", "serde", "serde_json", "slog-error-chain", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "uuid", ] @@ -7156,10 +7579,10 @@ version = "0.1.0" dependencies = [ "anyhow", "chrono", - "ereport-types", + "ereport-types 0.1.0", "iddqd", "nexus-reconfigurator-planning", - "nexus-types", + "nexus-types 0.1.0", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -7176,16 +7599,16 @@ dependencies = [ name = "nexus-internal-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "http", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "schemars 0.8.22", "serde", - "sled-agent-types-versions", + "sled-agent-types-versions 0.1.0", "uuid", ] @@ -7199,22 +7622,22 @@ dependencies = [ "chrono", "clickhouse-admin-keeper-client", "clickhouse-admin-server-client", - "clickhouse-admin-types", - "cockroach-admin-types", + "clickhouse-admin-types 0.1.0", + "cockroach-admin-types 0.1.0", "dns-service-client", "expectorate", "fmd-adm-sys", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-test-utils", - "gateway-types", + "gateway-types 0.1.0", "httpmock", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "itertools 0.14.0", - "nexus-client", - "nexus-types", + "nexus-client 0.1.0", + "nexus-types 0.1.0", "ntp-admin-client", "omicron-cockroach-metrics", "omicron-common", @@ -7227,9 +7650,9 @@ dependencies = [ "serde_json", "sled-agent-client", "sled-agent-resolvable-files-examples", - "sled-agent-types", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "strum 0.27.2", @@ -7245,17 +7668,17 @@ dependencies = [ name = "nexus-lockstep-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "http", - "nexus-types", - "nexus-types-versions", + "nexus-types 0.1.0", + "nexus-types-versions 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "schemars 0.8.22", "serde", - "sled-hardware-types", - "trust-quorum-types", + "sled-hardware-types 0.1.0", + "trust-quorum-types 0.1.0", "uuid", ] @@ -7263,13 +7686,13 @@ dependencies = [ name = "nexus-lockstep-client" version = "0.1.0" dependencies = [ - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "chrono", "futures", "iddqd", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-uuid-kinds", "omicron-workspace-hack", "oxnet", @@ -7279,9 +7702,9 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", "slog", "uuid", ] @@ -7311,7 +7734,7 @@ dependencies = [ "nexus-db-queries", "nexus-db-schema", "nexus-test-utils", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-test-utils", @@ -7333,19 +7756,19 @@ dependencies = [ "assert_matches", "camino", "chrono", - "dropshot 0.17.0", + "dropshot", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-test-utils", - "gateway-types", + "gateway-types 0.1.0", "http", "hubtools 0.4.7 (git+https://github.com/oxidecomputer/hubtools.git?rev=2b1ef9b38d75563ea800baa3b17327eec17b1b7a)", "iddqd", - "illumos-utils", - "internal-dns-resolver", - "internal-dns-types", - "nexus-types", + "illumos-utils 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", + "nexus-types 0.1.0", "omicron-common", "omicron-test-utils", "omicron-uuid-kinds", @@ -7358,10 +7781,10 @@ dependencies = [ "sha2", "sled-agent-api", "sled-agent-client", - "sled-agent-types", - "sled-agent-types-versions", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", "sled-diagnostics", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "sp-sim", @@ -7369,7 +7792,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "uuid", ] @@ -7380,9 +7803,9 @@ version = "0.1.0" dependencies = [ "anyhow", "futures", - "gateway-client", - "internal-dns-resolver", - "internal-dns-types", + "gateway-client 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnetwork", "nexus-db-lookup", "nexus-db-queries", @@ -7394,8 +7817,8 @@ dependencies = [ "pq-sys", "reqwest 0.13.2", "sled-agent-client", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "static_assertions", "thiserror 2.0.18", @@ -7408,12 +7831,12 @@ version = "0.1.0" dependencies = [ "ipnet", "nexus-reconfigurator-planning", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", - "sled-agent-types", + "sled-agent-types 0.1.0", "tufaceous-artifact", ] @@ -7431,7 +7854,7 @@ dependencies = [ "nexus-reconfigurator-preparation", "nexus-test-utils", "nexus-test-utils-macros", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-nexus", "omicron-rpaths", @@ -7459,15 +7882,15 @@ dependencies = [ "clickhouse-admin-keeper-client", "clickhouse-admin-server-client", "clickhouse-admin-single-client", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "cockroach-admin-client", "diesel", "fmd-adm-sys", "futures", "httptest", "iddqd", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnet", "ipnetwork", "newtype-uuid", @@ -7483,7 +7906,7 @@ dependencies = [ "nexus-reconfigurator-preparation", "nexus-test-utils", "nexus-test-utils-macros", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-nexus", "omicron-rpaths", @@ -7494,11 +7917,11 @@ dependencies = [ "pq-sys", "reqwest 0.13.2", "sled-agent-client", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "tokio", - "update-engine", + "update-engine 0.1.0", "uuid", ] @@ -7512,21 +7935,21 @@ dependencies = [ "camino-tempfile", "chrono", "clap", - "clickhouse-admin-types", - "cockroach-admin-types", + "clickhouse-admin-types 0.1.0", + "cockroach-admin-types 0.1.0", "daft", "debug-ignore", - "dropshot 0.17.0", + "dropshot", "expectorate", - "gateway-client", - "gateway-types", + "gateway-client 0.1.0", + "gateway-types 0.1.0", "hex-literal", "hickory-resolver 0.25.2", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "indexmap 2.14.0", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnet", "itertools 0.14.0", "maplit", @@ -7534,7 +7957,7 @@ dependencies = [ "nexus-inventory", "nexus-reconfigurator-blippy", "nexus-reconfigurator-simulation", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-deployment-graph", "omicron-test-utils", @@ -7547,8 +7970,8 @@ dependencies = [ "reconfigurator-cli", "semver 1.0.28", "sled-agent-client", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "sp-sim", @@ -7576,14 +7999,14 @@ dependencies = [ "futures", "nexus-db-model", "nexus-db-queries", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-uuid-kinds", "omicron-workspace-hack", "pq-sys", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", ] @@ -7598,7 +8021,7 @@ dependencies = [ "nexus-db-model", "nexus-db-queries", "nexus-db-schema", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-test-utils", @@ -7625,13 +8048,13 @@ dependencies = [ "itertools 0.14.0", "nexus-inventory", "nexus-reconfigurator-planning", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "petname", "sapling-renderdag", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "strum 0.27.2", "swrite", @@ -7654,7 +8077,7 @@ dependencies = [ "nexus-db-queries", "nexus-test-utils", "nexus-test-utils-macros", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-test-utils", @@ -7675,16 +8098,16 @@ name = "nexus-test-interface" version = "0.1.0" dependencies = [ "async-trait", - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "nexus-config", "nexus-db-queries", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-uuid-kinds", "omicron-workspace-hack", "pq-sys", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "tokio", "uuid", @@ -7695,15 +8118,15 @@ name = "nexus-test-utils" version = "0.1.0" dependencies = [ "anyhow", - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "bytes", "camino", "camino-tempfile", "chrono", "crucible-agent-client", "dns-service-client", - "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da)", - "dropshot 0.17.0", + "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be)", + "dropshot", "fmd-adm-sys", "futures", "gateway-messages", @@ -7714,18 +8137,18 @@ dependencies = [ "http-body-util", "hyper", "iddqd", - "illumos-utils", - "internal-dns-resolver", - "internal-dns-types", - "nexus-client", + "illumos-utils 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", + "nexus-client 0.1.0", "nexus-config", "nexus-db-queries", "nexus-lockstep-client", "nexus-test-interface", - "nexus-types", + "nexus-types 0.1.0", "omicron-cockroach-admin", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-rpaths", "omicron-sled-agent", "omicron-test-utils", @@ -7733,7 +8156,7 @@ dependencies = [ "omicron-workspace-hack", "oximeter 0.1.0", "oximeter-collector", - "oximeter-producer", + "oximeter-producer 0.1.0", "oxnet", "pq-sys", "pretty_assertions", @@ -7741,7 +8164,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sled-agent-client", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "tokio", @@ -7765,57 +8188,131 @@ name = "nexus-types" version = "0.1.0" dependencies = [ "anyhow", - "api_identity", + "api_identity 0.1.0", + "async-trait", + "base64 0.22.1", + "bootstrap-agent-lockstep-types 0.1.0", + "chrono", + "clap", + "clickhouse-admin-types 0.1.0", + "cockroach-admin-types 0.1.0", + "cookie", + "daft", + "derive-where", + "derive_more 0.99.20", + "dropshot", + "either", + "ereport-types 0.1.0", + "expectorate", + "futures", + "gateway-client 0.1.0", + "gateway-types 0.1.0", + "http", + "humantime", + "iddqd", + "illumos-utils 0.1.0", + "indent_write", + "internal-dns-types 0.1.0", + "ipnet", + "ipnetwork", + "itertools 0.14.0", + "newtype-uuid", + "newtype_derive", + "nexus-types-versions 0.1.0", + "omicron-common", + "omicron-passwords 0.1.0", + "omicron-test-utils", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "openssl", + "oximeter-db 0.1.0", + "oxnet", + "oxql-types 0.1.0", + "parse-display", + "proptest", + "regex", + "schemars 0.8.22", + "semver 1.0.28", + "serde", + "serde_json", + "serde_with", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", + "slog", + "slog-error-chain", + "steno", + "strum 0.27.2", + "swrite", + "tabled 0.15.0", + "test-strategy", + "textwrap 0.16.2", + "thiserror 2.0.18", + "tokio", + "tough", + "trust-quorum-protocol 0.1.0", + "trust-quorum-types 0.1.0", + "tufaceous-artifact", + "unicode-width 0.1.14", + "update-engine 0.1.0", + "url", + "uuid", +] + +[[package]] +name = "nexus-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "api_identity 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "async-trait", "base64 0.22.1", - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "chrono", "clap", - "clickhouse-admin-types", - "cockroach-admin-types", + "clickhouse-admin-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "cockroach-admin-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "cookie", "daft", "derive-where", "derive_more 0.99.20", - "dropshot 0.17.0", + "dropshot", "either", - "ereport-types", - "expectorate", + "ereport-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "futures", - "gateway-client", - "gateway-types", + "gateway-client 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "gateway-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "http", "humantime", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "indent_write", - "internal-dns-types", + "internal-dns-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "ipnet", "ipnetwork", "itertools 0.14.0", "newtype-uuid", "newtype_derive", - "nexus-types-versions", + "nexus-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-common", - "omicron-passwords", - "omicron-test-utils", + "omicron-passwords 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-uuid-kinds", "omicron-workspace-hack", "openssl", - "oximeter-db", + "oximeter-db 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "oxnet", - "oxql-types", + "oxql-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "parse-display", - "proptest", "regex", "schemars 0.8.22", "semver 1.0.28", "serde", "serde_json", "serde_with", - "sled-agent-types", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-agent-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "slog", "slog-error-chain", "steno", @@ -7827,11 +8324,48 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tough", - "trust-quorum-protocol", - "trust-quorum-types", + "trust-quorum-protocol 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "trust-quorum-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "tufaceous-artifact", "unicode-width 0.1.14", - "update-engine", + "update-engine 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "url", + "uuid", +] + +[[package]] +name = "nexus-types-versions" +version = "0.1.0" +dependencies = [ + "anyhow", + "api_identity 0.1.0", + "base64 0.22.1", + "chrono", + "daft", + "dropshot", + "http", + "mg-admin-client 0.1.0 (git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8)", + "mg-api-types", + "omicron-common", + "omicron-passwords 0.1.0", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "openssl", + "oxnet", + "oxql-types 0.1.0", + "parse-display", + "regex", + "schemars 0.8.22", + "semver 1.0.28", + "serde", + "serde_json", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", + "slog-error-chain", + "strum 0.27.2", + "thiserror 2.0.18", + "tough", + "tufaceous-artifact", "url", "uuid", ] @@ -7839,30 +8373,31 @@ dependencies = [ [[package]] name = "nexus-types-versions" version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "anyhow", - "api_identity", + "api_identity 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "base64 0.22.1", "chrono", "daft", - "dropshot 0.17.0", + "dropshot", "http", - "mg-admin-client", + "mg-admin-client 0.1.0 (git+https://github.com/oxidecomputer/maghemite?rev=7696ee48d5ee29a917dea459e281fe2e8ff20513)", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "omicron-uuid-kinds", "omicron-workspace-hack", "openssl", "oxnet", - "oxql-types", + "oxql-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "parse-display", "regex", "schemars 0.8.22", "semver 1.0.28", "serde", "serde_json", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "slog-error-chain", "strum 0.27.2", "thiserror 2.0.18", @@ -7905,18 +8440,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases 0.2.1", - "libc", -] - [[package]] name = "nix" version = "0.31.2" @@ -7946,6 +8469,15 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "nonempty" version = "0.12.0" @@ -7962,7 +8494,7 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" name = "ntp-admin-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "http", "ntp-admin-types-versions", @@ -8156,11 +8688,11 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ - "num_enum_derive 0.7.5", + "num_enum_derive 0.7.6", "rustversion", ] @@ -8178,9 +8710,9 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate 3.4.0", "proc-macro2", @@ -8275,13 +8807,13 @@ dependencies = [ "clap", "clickhouse-admin-api", "clickhouse-admin-test-utils", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "clickward", - "dropshot 0.17.0", + "dropshot", "expectorate", "flume", "http", - "illumos-utils", + "illumos-utils 0.1.0", "omicron-common", "omicron-test-utils", "omicron-uuid-kinds", @@ -8290,7 +8822,7 @@ dependencies = [ "openapiv3", "oxide-tokio-rt", "oximeter 0.1.0", - "oximeter-db", + "oximeter-db 0.1.0", "oximeter-test-utils", "schemars 0.8.22", "serde", @@ -8319,14 +8851,14 @@ dependencies = [ "chrono", "clap", "cockroach-admin-api", - "cockroach-admin-types", - "cockroach-admin-types-versions", + "cockroach-admin-types 0.1.0", + "cockroach-admin-types-versions 0.1.0", "csv", - "dropshot 0.17.0", + "dropshot", "expectorate", "fmd-adm-sys", "http", - "illumos-utils", + "illumos-utils 0.1.0", "nexus-test-utils", "omicron-common", "omicron-rpaths", @@ -8362,7 +8894,7 @@ dependencies = [ "anyhow", "chrono", "cockroach-admin-client", - "cockroach-admin-types", + "cockroach-admin-types 0.1.0", "futures", "omicron-workspace-hack", "parallel-task-set", @@ -8380,7 +8912,7 @@ name = "omicron-common" version = "0.1.0" dependencies = [ "anyhow", - "api_identity", + "api_identity 0.1.0", "async-trait", "backoff", "backon", @@ -8388,7 +8920,7 @@ dependencies = [ "camino-tempfile", "chrono", "daft", - "dropshot 0.17.0", + "dropshot", "expectorate", "futures", "hex", @@ -8399,12 +8931,12 @@ dependencies = [ "itertools 0.14.0", "libc", "macaddr", - "omicron-ledger", + "omicron-ledger 0.1.0", "omicron-uuid-kinds", "omicron-workspace-hack", "oxnet", "parse-display", - "progenitor-client 0.10.0", + "progenitor-client 0.13.0", "progenitor-client 0.14.0", "progenitor-extras", "proptest", @@ -8441,7 +8973,7 @@ dependencies = [ "progenitor-client 0.14.0", "reqwest 0.13.2", "serde", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -8465,11 +8997,11 @@ dependencies = [ "anyhow", "camino", "clap", - "dropshot 0.17.0", + "dropshot", "expectorate", "fmd-adm-sys", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-test-utils", "libc", "nexus-config", @@ -8541,22 +9073,22 @@ dependencies = [ "camino", "chrono", "clap", - "dropshot 0.17.0", - "ereport-types", + "dropshot", + "ereport-types 0.1.0", "expectorate", "futures", "gateway-api", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-sp-comms", "gateway-test-utils", - "gateway-types", - "gateway-types-versions", + "gateway-types 0.1.0", + "gateway-types-versions 0.1.0", "hex", "http", "hyper", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "ipcc", "omicron-common", "omicron-test-utils", @@ -8565,7 +9097,7 @@ dependencies = [ "oxide-tokio-rt", "oximeter 0.1.0", "oximeter-instruments", - "oximeter-producer", + "oximeter-producer 0.1.0", "schemars 0.8.22", "serde", "serde_json", @@ -8593,7 +9125,23 @@ dependencies = [ "async-trait", "camino", "camino-tempfile", - "dropshot 0.17.0", + "dropshot", + "omicron-workspace-hack", + "serde", + "serde_json", + "slog", + "slog-error-chain", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "omicron-ledger" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "async-trait", + "camino", "omicron-workspace-hack", "serde", "serde_json", @@ -8610,10 +9158,10 @@ dependencies = [ "anyhow", "assert_matches", "dns-service-client", - "dropshot 0.17.0", + "dropshot", "futures", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "live-tests-macros", "nexus-config", "nexus-db-model", @@ -8622,7 +9170,7 @@ dependencies = [ "nexus-lockstep-client", "nexus-reconfigurator-planning", "nexus-reconfigurator-preparation", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-test-utils", @@ -8631,7 +9179,7 @@ dependencies = [ "pq-sys", "reqwest 0.13.2", "serde", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "textwrap 0.16.2", @@ -8671,7 +9219,7 @@ dependencies = [ "async-bb8-diesel", "async-trait", "base64 0.22.1", - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "buf-list", "bytes", "camino", @@ -8691,17 +9239,17 @@ dependencies = [ "display-error-chain", "dns-server", "dns-service-client", - "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da)", - "dropshot 0.17.0", - "ereport-types", + "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be)", + "dropshot", + "ereport-types 0.1.0", "expectorate", "fatfs", "fmd-adm-sys", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-test-utils", - "gateway-types", + "gateway-types 0.1.0", "headers", "hex", "hickory-resolver 0.25.2", @@ -8715,9 +9263,9 @@ dependencies = [ "hyper-rustls", "hyper-staticfile", "iddqd", - "illumos-utils", - "internal-dns-resolver", - "internal-dns-types", + "illumos-utils 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnet", "ipnetwork", "itertools 0.14.0", @@ -8725,10 +9273,11 @@ dependencies = [ "lldpd-client", "macaddr", "maplit", - "mg-admin-client", + "mg-admin-client 0.1.0 (git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8)", + "mg-api-types", "nexus-auth", "nexus-background-task-interface", - "nexus-client", + "nexus-client 0.1.0", "nexus-config", "nexus-db-lookup", "nexus-db-model", @@ -8752,13 +9301,13 @@ dependencies = [ "nexus-test-interface", "nexus-test-utils", "nexus-test-utils-macros", - "nexus-types", - "nexus-types-versions", + "nexus-types 0.1.0", + "nexus-types-versions 0.1.0", "ntp-admin-client", "num-integer", "omicron-cockroach-metrics", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-rpaths", "omicron-sled-agent", "omicron-test-utils", @@ -8772,11 +9321,11 @@ dependencies = [ "oximeter 0.1.0", "oximeter-client", "oximeter-collector", - "oximeter-db", + "oximeter-db 0.1.0", "oximeter-instruments", - "oximeter-producer", + "oximeter-producer 0.1.0", "oxnet", - "oxql-types", + "oxql-types 0.1.0", "parallel-task-set", "parse-display", "paste", @@ -8792,10 +9341,8 @@ dependencies = [ "range-requests", "raw-cpuid", "rcgen", - "rdb-types", "ref-cast", "regex", - "reqwest 0.12.28", "reqwest 0.13.2", "ring", "rustls 0.22.4", @@ -8813,9 +9360,9 @@ dependencies = [ "sha2", "similar-asserts", "sled-agent-client", - "sled-agent-types", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-async", "slog-dtrace", @@ -8836,12 +9383,12 @@ dependencies = [ "tokio-test", "tokio-util", "tough", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "tufaceous", "tufaceous-artifact", "tufaceous-lib", "update-common", - "update-engine", + "update-engine 0.1.0", "usdt 0.5.0", "uuid", "zip 4.6.1", @@ -8855,7 +9402,7 @@ dependencies = [ "camino", "chrono", "clap", - "dropshot 0.17.0", + "dropshot", "expectorate", "fmd-adm-sys", "http", @@ -8910,23 +9457,23 @@ dependencies = [ "csv", "daft", "diesel", - "dropshot 0.17.0", + "dropshot", "dyn-clone", - "ereport-types", + "ereport-types 0.1.0", "expectorate", "fmd-adm-sys", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-test-utils", - "gateway-types", + "gateway-types 0.1.0", "http", "humantime", "iddqd", "indent_write", "indicatif", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnetwork", "itertools 0.14.0", "multimap", @@ -8942,7 +9489,7 @@ dependencies = [ "nexus-saga-recovery", "nexus-test-utils", "nexus-test-utils-macros", - "nexus-types", + "nexus-types 0.1.0", "ntp-admin-client", "omicron-common", "omicron-nexus", @@ -8953,7 +9500,7 @@ dependencies = [ "owo-colors 4.3.0", "oxide-tokio-rt", "oximeter-client", - "oximeter-db", + "oximeter-db 0.1.0", "oxnet", "petgraph 0.8.3", "pq-sys", @@ -8964,8 +9511,8 @@ dependencies = [ "serde_json", "sigpipe", "sled-agent-client", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "steno", @@ -8976,10 +9523,10 @@ dependencies = [ "tabled 0.15.0", "textwrap 0.16.2", "tokio", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "unicode-width 0.1.14", - "update-engine", + "update-engine 0.1.0", "url", "uuid", "vergen-gitcl", @@ -8997,7 +9544,7 @@ dependencies = [ "expectorate", "futures", "hex", - "illumos-utils", + "illumos-utils 0.1.0", "indent_write", "indicatif", "omicron-workspace-hack", @@ -9042,6 +9589,21 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "omicron-passwords" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "argon2", + "omicron-workspace-hack", + "rand 0.9.2", + "schemars 0.8.22", + "secrecy 0.10.3", + "serde", + "serde_with", + "thiserror 2.0.18", +] + [[package]] name = "omicron-pins" version = "0.1.0" @@ -9062,14 +9624,14 @@ dependencies = [ "anyhow", "camino", "clap", - "dropshot 0.17.0", - "internal-dns-resolver", - "internal-dns-types", + "dropshot", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "nexus-db-model", "nexus-db-queries", "nexus-mgs-updates", "nexus-reconfigurator-execution", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-uuid-kinds", @@ -9082,7 +9644,7 @@ dependencies = [ "slog", "supports-color 3.0.2", "tokio", - "update-engine", + "update-engine 0.1.0", "uuid", ] @@ -9141,7 +9703,7 @@ dependencies = [ "bytes", "camino", "clap", - "dropshot 0.17.0", + "dropshot", "futures", "libc", "omicron-common", @@ -9174,11 +9736,11 @@ dependencies = [ "async-trait", "atomicwrites", "base64 0.22.1", - "bootstore", + "bootstore 0.1.0", "bootstrap-agent-api", "bootstrap-agent-client", "bootstrap-agent-lockstep-api", - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "bytes", "camino", "camino-tempfile", @@ -9186,13 +9748,14 @@ dependencies = [ "cfg-if", "chrono", "clap", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "crucible-agent-client", + "crucible-pantry-client", "derive_more 0.99.20", "dice-verifier 0.3.0-pre0 (git+https://github.com/oxidecomputer/dice-util?branch=main)", "display-error-chain", "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=cc8e02a0800034c431c8cf96b889ea638da3d194)", - "dropshot 0.17.0", + "dropshot", "expectorate", "flate2", "flume", @@ -9209,23 +9772,23 @@ dependencies = [ "hyper", "hyper-staticfile", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "installinator-common", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "ipnetwork", "itertools 0.14.0", "key-manager", "libc", "macaddr", - "nexus-client", + "nexus-client 0.1.0", "nexus-config", "nexus-lockstep-client", - "nexus-types", + "nexus-types 0.1.0", "nix 0.31.2", "omicron-common", "omicron-ddm-admin-client", - "omicron-ledger", + "omicron-ledger 0.1.0", "omicron-rpaths", "omicron-test-utils", "omicron-uuid-kinds", @@ -9234,19 +9797,18 @@ dependencies = [ "oxide-tokio-rt", "oximeter 0.1.0", "oximeter-instruments", - "oximeter-producer", + "oximeter-producer 0.1.0", "oxnet", "pretty_assertions", "progenitor 0.14.0", "propolis-client", "propolis-mock-server", - "propolis_api_types", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", "rand 0.9.2", "range-requests", "regress 0.10.5", "repo-depot-api", "repo-depot-client", - "reqwest 0.12.28", "reqwest 0.13.2", "schemars 0.8.22", "secrecy 0.10.3", @@ -9263,11 +9825,11 @@ dependencies = [ "sled-agent-measurements", "sled-agent-rack-setup", "sled-agent-resolvable-files", - "sled-agent-types", - "sled-agent-types-versions", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", "sled-diagnostics", "sled-hardware", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "sled-storage", "slog", "slog-async", @@ -9289,8 +9851,8 @@ dependencies = [ "toml 0.8.23", "transient-dns-server", "trust-quorum", - "trust-quorum-protocol", - "trust-quorum-types", + "trust-quorum-protocol 0.1.0", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "tufaceous-brand-metadata", "usdt 0.5.0", @@ -9312,7 +9874,7 @@ dependencies = [ "camino", "camino-tempfile", "chrono", - "dropshot 0.17.0", + "dropshot", "expectorate", "filetime", "futures", @@ -9364,6 +9926,7 @@ version = "0.1.0" dependencies = [ "ahash", "aho-corasick", + "anstream 0.6.21", "anyhow", "aws-lc-rs", "base16ct", @@ -9508,7 +10071,6 @@ dependencies = [ "tokio-util", "toml 0.7.8", "toml_datetime 0.6.11", - "toml_datetime 0.7.5+spec-1.1.0", "toml_edit 0.19.15", "toml_edit 0.22.27", "toml_parser", @@ -9520,7 +10082,7 @@ dependencies = [ "usdt-impl 0.6.0", "uuid", "vergen", - "vergen-lib", + "vergen-lib 9.1.0", "winnow 0.7.14", "x509-cert", "zerocopy 0.8.40", @@ -9614,14 +10176,15 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.79" +version = "0.10.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf0b434746ee2832f4f0baf10137e1cabb18cbe6912c69e2e33263c45250f542" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" dependencies = [ "bitflags 2.11.0", "cfg-if", "foreign-types 0.3.2", "libc", + "once_cell", "openssl-macros", "openssl-sys", ] @@ -9651,9 +10214,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-sys" -version = "0.9.115" +version = "0.9.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "158fe5b292746440aa6e7a7e690e55aeb72d41505e2804c23c6973ad0e9c9781" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" dependencies = [ "cc", "libc", @@ -9825,7 +10388,7 @@ dependencies = [ [[package]] name = "oximeter" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#ccede3cb5287e447b0f30916000c7943b1596689" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "anyhow", "chrono", @@ -9845,7 +10408,7 @@ dependencies = [ name = "oximeter-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "omicron-common", "omicron-workspace-hack", @@ -9877,14 +10440,14 @@ dependencies = [ "camino", "chrono", "clap", - "dropshot 0.17.0", + "dropshot", "expectorate", "futures", "httpmock", - "internal-dns-resolver", - "internal-dns-types", - "nexus-client", - "nexus-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", + "nexus-client 0.1.0", + "nexus-types 0.1.0", "omicron-common", "omicron-test-utils", "omicron-workspace-hack", @@ -9894,7 +10457,7 @@ dependencies = [ "oximeter 0.1.0", "oximeter-api", "oximeter-client", - "oximeter-db", + "oximeter-db 0.1.0", "oximeter-types 0.1.0", "proptest", "qorb", @@ -9933,14 +10496,14 @@ dependencies = [ "chrono", "chrono-tz", "clap", - "clickhouse-admin-types", + "clickhouse-admin-types 0.1.0", "clickward", "const_format", "criterion", "crossterm 0.29.0", "debug-ignore", "display-error-chain", - "dropshot 0.17.0", + "dropshot", "expectorate", "futures", "gethostname", @@ -9950,7 +10513,7 @@ dependencies = [ "indexmap 2.14.0", "itertools 0.14.0", "libc", - "nom", + "nom 7.1.3", "num", "omicron-common", "omicron-test-utils", @@ -9958,7 +10521,7 @@ dependencies = [ "oxide-tokio-rt", "oximeter 0.1.0", "oximeter-test-utils", - "oxql-types", + "oxql-types 0.1.0", "parse-display", "peg", "qorb", @@ -9989,6 +10552,61 @@ dependencies = [ "uuid", ] +[[package]] +name = "oximeter-db" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "async-recursion", + "async-trait", + "bcs", + "bytes", + "camino", + "chrono", + "chrono-tz", + "clap", + "clickhouse-admin-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "clickward", + "const_format", + "debug-ignore", + "dropshot", + "futures", + "gethostname", + "highway", + "iana-time-zone", + "iddqd", + "indexmap 2.14.0", + "libc", + "nom 7.1.3", + "num", + "omicron-common", + "omicron-workspace-hack", + "oxide-tokio-rt", + "oximeter 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "oxql-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "parse-display", + "qorb", + "quote", + "regex", + "reqwest 0.13.2", + "schemars 0.8.22", + "serde", + "serde_json", + "slog", + "slog-async", + "slog-dtrace", + "slog-error-chain", + "slog-term", + "strum 0.27.2", + "termtree", + "thiserror 2.0.18", + "tokio", + "tokio-util", + "usdt 0.5.0", + "uuid", +] + [[package]] name = "oximeter-instruments" version = "0.1.0" @@ -9996,11 +10614,11 @@ dependencies = [ "anyhow", "cfg-if", "chrono", - "dropshot 0.17.0", + "dropshot", "futures", "http", "hyper", - "illumos-utils", + "illumos-utils 0.1.0", "kstat-rs", "libc", "omicron-common", @@ -10032,7 +10650,7 @@ dependencies = [ [[package]] name = "oximeter-macro-impl" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#ccede3cb5287e447b0f30916000c7943b1596689" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "omicron-workspace-hack", "proc-macro2", @@ -10047,16 +10665,16 @@ dependencies = [ "anyhow", "chrono", "clap", - "dropshot 0.17.0", + "dropshot", "either", - "internal-dns-resolver", - "internal-dns-types", - "nexus-client", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", + "nexus-client 0.1.0", "omicron-common", "omicron-test-utils", "omicron-workspace-hack", "oximeter 0.1.0", - "oximeter-producer-api", + "oximeter-producer-api 0.1.0", "schemars 0.8.22", "serde", "serde_json", @@ -10069,15 +10687,50 @@ dependencies = [ "uuid", ] +[[package]] +name = "oximeter-producer" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "chrono", + "dropshot", + "either", + "internal-dns-resolver 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "internal-dns-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "nexus-client 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "omicron-common", + "omicron-workspace-hack", + "oximeter 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "oximeter-producer-api 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "schemars 0.8.22", + "serde", + "slog", + "slog-dtrace", + "slog-error-chain", + "thiserror 2.0.18", + "tokio", + "uuid", +] + [[package]] name = "oximeter-producer-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "omicron-workspace-hack", "oximeter-types-versions 0.1.0", ] +[[package]] +name = "oximeter-producer-api" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "dropshot", + "omicron-workspace-hack", + "oximeter-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", +] + [[package]] name = "oximeter-schema" version = "0.1.0" @@ -10101,7 +10754,7 @@ dependencies = [ [[package]] name = "oximeter-schema" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#ccede3cb5287e447b0f30916000c7943b1596689" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "anyhow", "chrono", @@ -10128,7 +10781,7 @@ dependencies = [ "clickward", "omicron-test-utils", "omicron-workspace-hack", - "oximeter-db", + "oximeter-db 0.1.0", "oximeter-macro-impl 0.1.0", "oximeter-types 0.1.0", "slog", @@ -10150,7 +10803,7 @@ dependencies = [ [[package]] name = "oximeter-timeseries-macro" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#ccede3cb5287e447b0f30916000c7943b1596689" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "omicron-workspace-hack", "oximeter-schema 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", @@ -10179,22 +10832,10 @@ dependencies = [ [[package]] name = "oximeter-types" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#ccede3cb5287e447b0f30916000c7943b1596689" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ - "bytes", - "chrono", - "float-ord", - "num", - "omicron-common", "omicron-workspace-hack", "oximeter-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", - "parse-display", - "regex", - "schemars 0.8.22", - "serde", - "strum 0.27.2", - "thiserror 2.0.18", - "uuid", ] [[package]] @@ -10222,13 +10863,20 @@ dependencies = [ [[package]] name = "oximeter-types-versions" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/omicron?branch=main#ccede3cb5287e447b0f30916000c7943b1596689" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ + "bytes", "chrono", + "float-ord", + "num", "omicron-common", "omicron-workspace-hack", + "parse-display", + "regex", "schemars 0.8.22", "serde", + "strum 0.27.2", + "thiserror 2.0.18", "uuid", ] @@ -10250,9 +10898,9 @@ dependencies = [ [[package]] name = "oxnet" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dc6fb07ecd6d2a17ff1431bc5b3ce11036c0b6dd93a3c4904db5b910817b162" +checksum = "057865b45bb202b17ed475d8f22f0416412de2c317c168fefecf9d207faf048d" dependencies = [ "ipnetwork", "schemars 0.8.22", @@ -10276,6 +10924,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "oxql-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "chrono", + "highway", + "num", + "omicron-workspace-hack", + "oximeter-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "schemars 0.8.22", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "p256" version = "0.13.2" @@ -11143,17 +11808,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "progenitor" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced2eadb9776a201d0585b4b072fd44d7d2104e0f3452d967b5a78966f4855cf" -dependencies = [ - "progenitor-client 0.10.0", - "progenitor-impl 0.10.0", - "progenitor-macro 0.10.0", -] - [[package]] name = "progenitor" version = "0.11.2" @@ -11187,21 +11841,6 @@ dependencies = [ "progenitor-macro 0.14.0", ] -[[package]] -name = "progenitor-client" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "296003fd74e64c77aeb2c10eae850eb543211a8557dd3b3de6f4230b5071e44b" -dependencies = [ - "bytes", - "futures-core", - "percent-encoding", - "reqwest 0.12.28", - "serde", - "serde_json", - "serde_urlencoded", -] - [[package]] name = "progenitor-client" version = "0.11.2" @@ -11249,36 +11888,14 @@ dependencies = [ [[package]] name = "progenitor-extras" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a674beb05f5de307ba7a3a6cddeffb262ab4a7712e620112d6f53c3259f328" -dependencies = [ - "backon", - "http", - "progenitor-client 0.14.0", - "tokio", -] - -[[package]] -name = "progenitor-impl" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b17e5363daa50bf1cccfade6b0fb970d2278758fd5cfa9ab69f25028e4b1afa3" -dependencies = [ - "heck 0.5.0", - "http", - "indexmap 2.14.0", - "openapiv3", - "proc-macro2", - "quote", - "regex", - "schemars 0.8.22", - "serde", - "serde_json", - "syn 2.0.117", - "thiserror 2.0.18", - "typify 0.4.3", - "unicode-ident", +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a674beb05f5de307ba7a3a6cddeffb262ab4a7712e620112d6f53c3259f328" +dependencies = [ + "backon", + "http", + "progenitor-client 0.14.0", + "tokio", ] [[package]] @@ -11347,24 +11964,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "progenitor-macro" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4972aec926d1e06d6abc11ab3f063d2f7063be3dd46fd2839442c14d8e48f3ed" -dependencies = [ - "openapiv3", - "proc-macro2", - "progenitor-impl 0.10.0", - "quote", - "schemars 0.8.22", - "serde", - "serde_json", - "serde_tokenstream", - "serde_yaml", - "syn 2.0.117", -] - [[package]] name = "progenitor-macro" version = "0.11.2" @@ -11419,13 +12018,26 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "propolis-api-types-versions" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" +dependencies = [ + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=bd9a0e2abe6b6b89aec8c85f4ee57474144ed150)", + "propolis_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", + "schemars 0.8.22", + "serde", + "thiserror 1.0.69", + "uuid", +] + [[package]] name = "propolis-api-types-versions" version = "0.0.0" source = "git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845#bc489ddf0f38f75e0c194b86cf6f0de377f68845" dependencies = [ - "crucible-client-types", - "propolis_types", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "propolis_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845)", "schemars 0.8.22", "serde", "thiserror 1.0.69", @@ -11435,15 +12047,15 @@ dependencies = [ [[package]] name = "propolis-client" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845#bc489ddf0f38f75e0c194b86cf6f0de377f68845" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" dependencies = [ "async-trait", "base64 0.21.7", - "crucible-client-types", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=bd9a0e2abe6b6b89aec8c85f4ee57474144ed150)", "futures", - "progenitor 0.13.0", - "progenitor-client 0.13.0", - "propolis-api-types-versions", + "progenitor 0.14.0", + "progenitor-client 0.14.0", + "propolis-api-types-versions 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", "rand 0.9.2", "reqwest 0.13.2", "schemars 0.8.22", @@ -11459,19 +12071,19 @@ dependencies = [ [[package]] name = "propolis-mock-server" version = "0.0.0" -source = "git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845#bc489ddf0f38f75e0c194b86cf6f0de377f68845" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" dependencies = [ "anyhow", "atty", "base64 0.21.7", "clap", - "dropshot 0.17.0", + "dropshot", "futures", "hyper", - "progenitor 0.13.0", - "propolis-api-types-versions", - "propolis_api_types", - "propolis_types", + "progenitor 0.14.0", + "propolis-api-types-versions 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", + "propolis_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", "rand 0.9.2", "reqwest 0.13.2", "schemars 0.8.22", @@ -11489,13 +12101,31 @@ dependencies = [ "uuid", ] +[[package]] +name = "propolis_api_types" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" +dependencies = [ + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=bd9a0e2abe6b6b89aec8c85f4ee57474144ed150)", + "propolis-api-types-versions 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", +] + [[package]] name = "propolis_api_types" version = "0.0.0" source = "git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845#bc489ddf0f38f75e0c194b86cf6f0de377f68845" dependencies = [ - "crucible-client-types", - "propolis-api-types-versions", + "crucible-client-types 0.1.0 (git+https://github.com/oxidecomputer/crucible?rev=ae1da83e66c648574827298f4bc444632bf4d047)", + "propolis-api-types-versions 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845)", +] + +[[package]] +name = "propolis_types" +version = "0.0.0" +source = "git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd#75518ad2c26db9278895ab1262c28daf13bfdffd" +dependencies = [ + "schemars 0.8.22", + "serde", ] [[package]] @@ -11529,7 +12159,7 @@ dependencies = [ [[package]] name = "protocol" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/lldp#d22509dfdb051321b859e924948605115691b93c" +source = "git+https://github.com/oxidecomputer/lldp#0d199cebf8fe3b24b9d943bdc46a00c227e5a345" dependencies = [ "anyhow", "schemars 0.8.22", @@ -11721,9 +12351,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20 0.10.0", "getrandom 0.4.1", @@ -11807,7 +12437,7 @@ name = "range-requests" version = "0.1.0" dependencies = [ "bytes", - "dropshot 0.17.0", + "dropshot", "futures", "http", "http-body", @@ -11935,20 +12565,20 @@ dependencies = [ "colored 2.2.0", "daft", "datatest-stable", - "dropshot 0.17.0", + "dropshot", "expectorate", - "gateway-types", + "gateway-types 0.1.0", "humantime", "iddqd", "indent_write", - "internal-dns-types", + "internal-dns-types 0.1.0", "itertools 0.14.0", "newtype-uuid", "nexus-inventory", "nexus-reconfigurator-blippy", "nexus-reconfigurator-planning", "nexus-reconfigurator-simulation", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-repl-utils", "omicron-rpaths", @@ -11959,7 +12589,7 @@ dependencies = [ "semver 1.0.28", "serde", "serde_json", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "slog-term", @@ -11982,23 +12612,23 @@ dependencies = [ "anyhow", "chrono", "clap", - "dropshot 0.17.0", + "dropshot", "futures", - "gateway-client", - "gateway-types", + "gateway-client 0.1.0", + "gateway-types 0.1.0", "humantime", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "nexus-mgs-updates", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-repl-utils", "omicron-workspace-hack", "oxide-tokio-rt", "qorb", "serde_json", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "swrite", "tokio", @@ -12168,7 +12798,7 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" name = "repo-depot-api" version = "0.1.0" dependencies = [ - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "omicron-workspace-hack", "schemars 0.8.22", @@ -12246,6 +12876,7 @@ dependencies = [ "bytes", "cookie", "cookie_store", + "encoding_rs", "futures-channel", "futures-core", "futures-util", @@ -12258,6 +12889,7 @@ dependencies = [ "hyper-util", "js-sys", "log", + "mime", "percent-encoding", "pin-project-lite", "quinn", @@ -12929,7 +13561,7 @@ source = "git+https://github.com/oxidecomputer/scim2-rs?rev=163606c052ee30b16dfa dependencies = [ "anyhow", "chrono", - "dropshot 0.17.0", + "dropshot", "http", "iddqd", "schemars 0.8.22", @@ -13521,7 +14153,7 @@ version = "0.1.0" dependencies = [ "camino", "chrono", - "dropshot 0.17.0", + "dropshot", "dropshot-api-manager-types", "http", "iddqd", @@ -13531,12 +14163,12 @@ dependencies = [ "schemars 0.8.22", "semver 1.0.28", "serde", - "sled-agent-types", - "sled-agent-types-versions", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", "sled-diagnostics", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog-error-chain", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "uuid", "x509-cert", @@ -13560,11 +14192,11 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", "slog", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "uuid", ] @@ -13583,19 +14215,19 @@ dependencies = [ "chrono", "debug-ignore", "derive_more 0.99.20", - "dropshot 0.17.0", + "dropshot", "either", "expectorate", "futures", "glob", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "installinator-common", "key-manager", - "key-manager-types", + "key-manager-types 0.1.0", "ntp-admin-v1-client", "omicron-common", - "omicron-ledger", + "omicron-ledger 0.1.0", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -13610,8 +14242,8 @@ dependencies = [ "sha2", "sled-agent-api", "sled-agent-resolvable-files-examples", - "sled-agent-types", - "sled-agent-types-versions", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", "sled-hardware", "sled-storage", "slog", @@ -13620,7 +14252,7 @@ dependencies = [ "test-strategy", "thiserror 2.0.18", "tokio", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "tufaceous-artifact", "xshell", "zone", @@ -13633,17 +14265,17 @@ dependencies = [ "anyhow", "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=cc8e02a0800034c431c8cf96b889ea638da3d194)", "futures", - "gateway-client", + "gateway-client 0.1.0", "http", - "internal-dns-resolver", - "internal-dns-types", - "mg-admin-client", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", + "mg-admin-client 0.1.0 (git+https://github.com/oxidecomputer/maghemite?rev=5060d5864df864741dac8a0a4078fa4ecc3c73c8)", + "mg-api-types", "omicron-common", "omicron-ddm-admin-client", "omicron-workspace-hack", "oxnet", - "rdb-types", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -13658,15 +14290,15 @@ dependencies = [ "async-trait", "chrono", "derive_more 0.99.20", - "dropshot 0.17.0", + "dropshot", "futures", - "illumos-utils", + "illumos-utils 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", "serde", "sled-agent-api", - "sled-agent-types", + "sled-agent-types 0.1.0", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -13686,8 +14318,8 @@ dependencies = [ "serde", "sled-agent-config-reconciler", "sled-agent-resolvable-files", - "sled-agent-types", - "sled-agent-types-versions", + "sled-agent-types 0.1.0", + "sled-agent-types-versions 0.1.0", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -13699,8 +14331,8 @@ name = "sled-agent-rack-setup" version = "0.1.0" dependencies = [ "anyhow", - "bootstore", - "bootstrap-agent-lockstep-types", + "bootstore 0.1.0", + "bootstrap-agent-lockstep-types 0.1.0", "camino", "camino-tempfile", "cancel-safe-futures", @@ -13710,17 +14342,17 @@ dependencies = [ "futures", "http", "iddqd", - "illumos-utils", - "internal-dns-resolver", - "internal-dns-types", + "illumos-utils 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "itertools 0.14.0", "nexus-lockstep-client", "nexus-reconfigurator-blippy", - "nexus-types", + "nexus-types 0.1.0", "ntp-admin-client", "omicron-common", "omicron-ddm-admin-client", - "omicron-ledger", + "omicron-ledger 0.1.0", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -13732,8 +14364,8 @@ dependencies = [ "serde", "sled-agent-client", "sled-agent-config-reconciler", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "sled-storage", "slog", "slog-error-chain", @@ -13742,8 +14374,8 @@ dependencies = [ "tokio", "toml 0.8.23", "trust-quorum", - "trust-quorum-protocol", - "trust-quorum-types", + "trust-quorum-protocol 0.1.0", + "trust-quorum-types 0.1.0", "uuid", ] @@ -13753,10 +14385,10 @@ version = "0.1.0" dependencies = [ "camino", "camino-tempfile-ext", - "dropshot 0.17.0", + "dropshot", "expectorate", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "omicron-common", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -13767,7 +14399,7 @@ dependencies = [ "sha2", "sled-agent-config-reconciler", "sled-agent-resolvable-files-examples", - "sled-agent-types", + "sled-agent-types 0.1.0", "sled-storage", "slog", "slog-error-chain", @@ -13787,7 +14419,7 @@ dependencies = [ "omicron-workspace-hack", "serde_json", "sha2", - "sled-agent-types", + "sled-agent-types 0.1.0", "sled-storage", "tufaceous-artifact", ] @@ -13798,7 +14430,7 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "bootstore", + "bootstore 0.1.0", "byte-wrapper", "camino", "chrono", @@ -13812,8 +14444,8 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", - "sled-agent-types-versions", - "sled-hardware-types", + "sled-agent-types-versions 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "swrite", @@ -13823,6 +14455,36 @@ dependencies = [ "uuid", ] +[[package]] +name = "sled-agent-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "async-trait", + "bootstore 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "byte-wrapper", + "camino", + "chrono", + "daft", + "iddqd", + "omicron-common", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "oxnet", + "schemars 0.8.22", + "serde", + "serde_json", + "sled-agent-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "slog", + "slog-error-chain", + "swrite", + "thiserror 2.0.18", + "tufaceous-artifact", + "uuid", +] + [[package]] name = "sled-agent-types-versions" version = "0.1.0" @@ -13830,7 +14492,7 @@ dependencies = [ "anyhow", "assert_matches", "async-trait", - "bootstore", + "bootstore 0.1.0", "byte-wrapper", "camino", "chrono", @@ -13840,28 +14502,67 @@ dependencies = [ "ipnetwork", "itertools 0.14.0", "omicron-common", - "omicron-ledger", - "omicron-passwords", + "omicron-ledger 0.1.0", + "omicron-passwords 0.1.0", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", "oxnet", - "propolis-api-types-versions", - "propolis_api_types", + "propolis-api-types-versions 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=75518ad2c26db9278895ab1262c28daf13bfdffd)", "proptest", "schemars 0.8.22", "serde", "serde_json", "serde_with", "sha3", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "strum 0.27.2", "test-strategy", "thiserror 2.0.18", "toml 0.8.23", - "trust-quorum-types-versions", + "trust-quorum-types-versions 0.1.0", + "tufaceous-artifact", + "uuid", +] + +[[package]] +name = "sled-agent-types-versions" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "async-trait", + "bootstore 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "byte-wrapper", + "camino", + "chrono", + "daft", + "iddqd", + "indent_write", + "ipnetwork", + "itertools 0.14.0", + "omicron-common", + "omicron-ledger 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "omicron-passwords 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "oxnet", + "propolis-api-types-versions 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845)", + "propolis_api_types 0.0.0 (git+https://github.com/oxidecomputer/propolis?rev=bc489ddf0f38f75e0c194b86cf6f0de377f68845)", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_with", + "sha3", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "slog", + "slog-error-chain", + "strum 0.27.2", + "thiserror 2.0.18", + "trust-quorum-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "tufaceous-artifact", "uuid", ] @@ -13876,7 +14577,7 @@ dependencies = [ "chrono", "fs-err 3.3.0", "futures", - "illumos-utils", + "illumos-utils 0.1.0", "jiff", "libc", "omicron-common", @@ -13909,7 +14610,7 @@ dependencies = [ "futures", "gethostname", "illumos-devinfo", - "illumos-utils", + "illumos-utils 0.1.0", "libc", "libefi-illumos", "libnvme", @@ -13921,7 +14622,7 @@ dependencies = [ "rand 0.9.2", "schemars 0.8.22", "serde", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -13945,6 +14646,19 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "sled-hardware-types" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "daft", + "omicron-workspace-hack", + "schemars 0.8.22", + "serde", + "slog", + "thiserror 2.0.18", +] + [[package]] name = "sled-storage" version = "0.1.0" @@ -13960,7 +14674,7 @@ dependencies = [ "futures", "glob", "iddqd", - "illumos-utils", + "illumos-utils 0.1.0", "key-manager", "omicron-common", "omicron-test-utils", @@ -14141,6 +14855,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "smf" +version = "0.10.0" +source = "git+https://github.com/illumos/smf-rs?branch=main#72389b5161d2cca1f0e239b337af68bf31f191f4" +dependencies = [ + "libc", + "libscf-sys", + "num-traits", + "thiserror 1.0.69", +] + [[package]] name = "smf" version = "0.10.0" @@ -14229,14 +14954,14 @@ dependencies = [ "anyhow", "async-trait", "clap", - "dropshot 0.17.0", + "dropshot", "futures", "gateway-ereport-messages", "gateway-messages", - "gateway-types", + "gateway-types 0.1.0", "hex", "hubtools 0.4.7 (git+https://github.com/oxidecomputer/hubtools.git?rev=2b1ef9b38d75563ea800baa3b17327eec17b1b7a)", - "nexus-types", + "nexus-types 0.1.0", "nix 0.31.2", "omicron-common", "omicron-workspace-hack", @@ -14595,18 +15320,18 @@ dependencies = [ "camino", "camino-tempfile", "chrono", - "dropshot 0.17.0", + "dropshot", "futures", - "gateway-client", - "gateway-types", - "internal-dns-resolver", - "internal-dns-types", + "gateway-client 0.1.0", + "gateway-types 0.1.0", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "jiff", "nexus-db-model", "nexus-db-queries", "nexus-networking", "nexus-reconfigurator-preparation", - "nexus-types", + "nexus-types 0.1.0", "omicron-common", "omicron-rpaths", "omicron-uuid-kinds", @@ -15206,7 +15931,7 @@ dependencies = [ [[package]] name = "tofino" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/tofino#7e56ab6e9a64ebae27cd97cd6e10ebf2cfdc3a33" +source = "git+https://github.com/oxidecomputer/tofino#e25e52991785039e967fd8fe7d86554d976e6d4b" dependencies = [ "anyhow", "cc", @@ -15405,21 +16130,6 @@ dependencies = [ "toml_edit 0.22.27", ] -[[package]] -name = "toml" -version = "0.9.12+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" -dependencies = [ - "indexmap 2.14.0", - "serde_core", - "serde_spanned 1.0.4", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "toml_writer", - "winnow 0.7.14", -] - [[package]] name = "toml" version = "1.0.6+spec-1.1.0" @@ -15623,10 +16333,10 @@ dependencies = [ "reconfigurator-cli", "reedline", "serde_json", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "tabled 0.15.0", - "trust-quorum-protocol", + "trust-quorum-protocol 0.1.0", "trust-quorum-test-utils", ] @@ -15676,7 +16386,7 @@ dependencies = [ [[package]] name = "transceiver-controller" version = "0.1.1" -source = "git+https://github.com/oxidecomputer/transceiver-control?branch=main#11afc484d5957b13d3058e44db274aa720cea1c4" +source = "git+https://github.com/oxidecomputer/transceiver-control?branch=main#81167659157860d6587713b3362b7bc791dfb530" dependencies = [ "anyhow", "clap", @@ -15724,7 +16434,7 @@ dependencies = [ [[package]] name = "transceiver-decode" version = "0.1.0" -source = "git+https://github.com/oxidecomputer/transceiver-control?branch=main#11afc484d5957b13d3058e44db274aa720cea1c4" +source = "git+https://github.com/oxidecomputer/transceiver-control?branch=main#81167659157860d6587713b3362b7bc791dfb530" dependencies = [ "schemars 0.8.22", "serde", @@ -15748,7 +16458,7 @@ dependencies = [ [[package]] name = "transceiver-messages" version = "0.1.1" -source = "git+https://github.com/oxidecomputer/transceiver-control?branch=main#11afc484d5957b13d3058e44db274aa720cea1c4" +source = "git+https://github.com/oxidecomputer/transceiver-control?branch=main#81167659157860d6587713b3362b7bc791dfb530" dependencies = [ "bitflags 2.11.0", "clap", @@ -15778,10 +16488,10 @@ dependencies = [ "anyhow", "dns-server", "dns-service-client", - "dropshot 0.17.0", + "dropshot", "hickory-proto 0.25.2", "hickory-resolver 0.25.2", - "internal-dns-types", + "internal-dns-types 0.1.0", "omicron-workspace-hack", "slog", "tempfile", @@ -15794,7 +16504,7 @@ dependencies = [ "anyhow", "assert_matches", "attest-mock", - "bootstore", + "bootstore 0.1.0", "bytes", "camino", "chacha20poly1305", @@ -15802,13 +16512,13 @@ dependencies = [ "daft", "debug-ignore", "derive_more 0.99.20", - "dropshot 0.17.0", + "dropshot", "futures", - "gfss", + "gfss 0.1.0", "hex", "hkdf", "iddqd", - "omicron-ledger", + "omicron-ledger 0.1.0", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -15820,8 +16530,8 @@ dependencies = [ "serde_with", "sha3", "sled-agent-measurements", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "sprockets-tls", @@ -15831,9 +16541,9 @@ dependencies = [ "test-strategy", "thiserror 2.0.18", "tokio", - "trust-quorum-protocol", + "trust-quorum-protocol 0.1.0", "trust-quorum-test-utils", - "trust-quorum-types", + "trust-quorum-types 0.1.0", "uuid", "zeroize", ] @@ -15844,15 +16554,15 @@ version = "0.1.0" dependencies = [ "assert_matches", "attest-mock", - "bootstore", + "bootstore 0.1.0", "bytes", "camino", "chacha20poly1305", "ciborium", "daft", "derive_more 0.99.20", - "dropshot 0.17.0", - "gfss", + "dropshot", + "gfss 0.1.0", "hex", "hkdf", "iddqd", @@ -15866,8 +16576,8 @@ dependencies = [ "serde_json", "serde_with", "sha3", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "static_assertions", @@ -15875,7 +16585,42 @@ dependencies = [ "test-strategy", "thiserror 2.0.18", "trust-quorum-test-utils", - "trust-quorum-types", + "trust-quorum-types 0.1.0", + "uuid", + "zeroize", +] + +[[package]] +name = "trust-quorum-protocol" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "bootstore 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "bytes", + "camino", + "chacha20poly1305", + "ciborium", + "daft", + "derive_more 0.99.20", + "gfss 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "hex", + "hkdf", + "iddqd", + "omicron-uuid-kinds", + "omicron-workspace-hack", + "rand 0.9.2", + "secrecy 0.10.3", + "serde", + "serde_with", + "sha3", + "sled-agent-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", + "slog", + "slog-error-chain", + "static_assertions", + "subtle", + "thiserror 2.0.18", + "trust-quorum-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "uuid", "zeroize", ] @@ -15884,40 +16629,71 @@ dependencies = [ name = "trust-quorum-test-utils" version = "0.1.0" dependencies = [ - "bootstore", + "bootstore 0.1.0", "camino", "daft", - "dropshot 0.17.0", - "gfss", + "dropshot", + "gfss 0.1.0", "iddqd", "omicron-uuid-kinds", "omicron-workspace-hack", "secrecy 0.10.3", "serde", "serde_json", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", - "trust-quorum-protocol", - "trust-quorum-types", + "trust-quorum-protocol 0.1.0", + "trust-quorum-types 0.1.0", +] + +[[package]] +name = "trust-quorum-types" +version = "0.1.0" +dependencies = [ + "omicron-workspace-hack", + "trust-quorum-types-versions 0.1.0", ] [[package]] name = "trust-quorum-types" version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "omicron-workspace-hack", + "trust-quorum-types-versions 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", +] + +[[package]] +name = "trust-quorum-types-versions" +version = "0.1.0" dependencies = [ + "byte-wrapper", + "daft", + "derive_more 0.99.20", + "gfss 0.1.0", + "iddqd", + "omicron-uuid-kinds", "omicron-workspace-hack", - "trust-quorum-types-versions", + "rand 0.9.2", + "schemars 0.8.22", + "serde", + "serde_with", + "sled-hardware-types 0.1.0", + "slog", + "slog-error-chain", + "thiserror 2.0.18", ] [[package]] name = "trust-quorum-types-versions" version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" dependencies = [ "byte-wrapper", "daft", "derive_more 0.99.20", - "gfss", + "gfss 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "iddqd", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -15925,7 +16701,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_with", - "sled-hardware-types", + "sled-hardware-types 0.1.0 (git+https://github.com/oxidecomputer/omicron?branch=main)", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -16362,7 +17138,7 @@ dependencies = [ "clap", "debug-ignore", "display-error-chain", - "dropshot 0.17.0", + "dropshot", "flate2", "fs-err 3.3.0", "futures", @@ -16425,6 +17201,36 @@ dependencies = [ "uuid", ] +[[package]] +name = "update-engine" +version = "0.1.0" +source = "git+https://github.com/oxidecomputer/omicron?branch=main#dbaeb64ed174748570548023bb4b119d939105b0" +dependencies = [ + "anyhow", + "cancel-safe-futures", + "chrono", + "debug-ignore", + "derive-where", + "either", + "futures", + "indent_write", + "indexmap 2.14.0", + "libsw", + "linear-map", + "omicron-workspace-hack", + "owo-colors 4.3.0", + "petgraph 0.8.3", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_with", + "slog", + "swrite", + "tokio", + "unicode-width 0.1.14", + "uuid", +] + [[package]] name = "url" version = "2.5.8" @@ -16612,24 +17418,24 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "9.0.6" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2bf58be11fc9414104c6d3a2e464163db5ef74b12296bda593cac37b6e4777" +checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" dependencies = [ "anyhow", - "cargo_metadata 0.19.2", + "cargo_metadata 0.23.1", "derive_builder", "regex", "rustc_version 0.4.1", "rustversion", - "vergen-lib", + "vergen-lib 9.1.0", ] [[package]] name = "vergen-git2" -version = "1.0.7" +version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f6ee511ec45098eabade8a0750e76eec671e7fb2d9360c563911336bea9cac1" +checksum = "d51ab55ddf1188c8d679f349775362b0fa9e90bd7a4ac69838b2a087623f0d57" dependencies = [ "anyhow", "derive_builder", @@ -16637,7 +17443,7 @@ dependencies = [ "rustversion", "time", "vergen", - "vergen-lib", + "vergen-lib 9.1.0", ] [[package]] @@ -16651,7 +17457,7 @@ dependencies = [ "rustversion", "time", "vergen", - "vergen-lib", + "vergen-lib 0.1.6", ] [[package]] @@ -16665,6 +17471,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "vergen-lib" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + [[package]] name = "version_check" version = "0.9.5" @@ -16988,7 +17805,7 @@ dependencies = [ "itertools 0.14.0", "maplit", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-workspace-hack", "owo-colors 4.3.0", "proptest", @@ -16999,8 +17816,8 @@ dependencies = [ "serde", "serde_json", "shell-words", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-async", "slog-envlogger", @@ -17018,7 +17835,7 @@ dependencies = [ "tufaceous-artifact", "tui-tree-widget", "unicode-width 0.1.14", - "update-engine", + "update-engine 0.1.0", "wicket-common", "wicketd-client", "zeroize", @@ -17029,10 +17846,10 @@ name = "wicket-common" version = "0.1.0" dependencies = [ "anyhow", - "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da)", - "dropshot 0.17.0", - "gateway-client", - "gateway-types", + "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be)", + "dropshot", + "gateway-client 0.1.0", + "gateway-types 0.1.0", "maplit", "omicron-common", "omicron-workspace-hack", @@ -17043,8 +17860,8 @@ dependencies = [ "serde", "serde_json", "sha2", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "slog-error-chain", "thiserror 2.0.18", @@ -17052,7 +17869,7 @@ dependencies = [ "toml 0.8.23", "transceiver-controller 0.1.1 (git+https://github.com/oxidecomputer/transceiver-control)", "tufaceous-artifact", - "update-engine", + "update-engine 0.1.0", ] [[package]] @@ -17094,37 +17911,37 @@ dependencies = [ "clap", "debug-ignore", "display-error-chain", - "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=50ed531201ea5f85115141a2203d551fbbdaa3da)", - "dropshot 0.17.0", + "dpd-client 0.1.0 (git+https://github.com/oxidecomputer/dendrite?rev=90efdc6cf618523320bc403f1b9484dbd7f762be)", + "dropshot", "either", "expectorate", "flate2", "flume", "fs-err 3.3.0", "futures", - "gateway-client", + "gateway-client 0.1.0", "gateway-messages", "gateway-test-utils", - "gateway-types", + "gateway-types 0.1.0", "hex", "hickory-resolver 0.25.2", "http", "http-body-util", "hubtools 0.4.7 (git+https://github.com/oxidecomputer/hubtools.git?rev=2b1ef9b38d75563ea800baa3b17327eec17b1b7a)", "hyper", - "illumos-utils", + "illumos-utils 0.1.0", "installinator", "installinator-api", "installinator-client", "installinator-common", - "internal-dns-resolver", - "internal-dns-types", + "internal-dns-resolver 0.1.0", + "internal-dns-types 0.1.0", "itertools 0.14.0", "maplit", "omicron-certificates", "omicron-common", "omicron-ddm-admin-client", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-test-utils", "omicron-uuid-kinds", "omicron-workspace-hack", @@ -17142,8 +17959,8 @@ dependencies = [ "sled-agent-config-reconciler", "sled-agent-resolvable-files", "sled-agent-resolvable-files-examples", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "sled-storage", "slog", "slog-dtrace", @@ -17161,7 +17978,7 @@ dependencies = [ "tufaceous-artifact", "tufaceous-lib", "update-common", - "update-engine", + "update-engine 0.1.0", "uuid", "wicket", "wicket-common", @@ -17174,16 +17991,16 @@ name = "wicketd-api" version = "0.1.0" dependencies = [ "bootstrap-agent-lockstep-client", - "dropshot 0.17.0", - "gateway-client", + "dropshot", + "gateway-client 0.1.0", "omicron-common", - "omicron-passwords", + "omicron-passwords 0.1.0", "omicron-uuid-kinds", "omicron-workspace-hack", "schemars 0.8.22", "semver 1.0.28", "serde", - "sled-hardware-types", + "sled-hardware-types 0.1.0", "slog", "tufaceous-artifact", "wicket-common", @@ -17193,7 +18010,7 @@ dependencies = [ name = "wicketd-client" version = "0.1.0" dependencies = [ - "bootstrap-agent-lockstep-types", + "bootstrap-agent-lockstep-types 0.1.0", "chrono", "installinator-common", "omicron-common", @@ -17206,10 +18023,10 @@ dependencies = [ "semver 1.0.28", "serde", "serde_json", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", - "update-engine", + "update-engine 0.1.0", "uuid", "wicket-common", ] @@ -17645,6 +18462,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" @@ -18019,7 +18845,7 @@ dependencies = [ "async-trait", "camino", "camino-tempfile", - "illumos-utils", + "illumos-utils 0.1.0", "key-manager", "omicron-common", "omicron-uuid-kinds", @@ -18091,15 +18917,15 @@ dependencies = [ "anyhow", "camino", "clap", - "dropshot 0.17.0", - "illumos-utils", + "dropshot", + "illumos-utils 0.1.0", "omicron-common", "omicron-workspace-hack", "oxide-tokio-rt", "oxnet", "serde_json", - "sled-agent-types", - "sled-hardware-types", + "sled-agent-types 0.1.0", + "sled-hardware-types 0.1.0", "slog", "tokio", "uzers", diff --git a/Cargo.toml b/Cargo.toml index 82999f4e95f..f99820863c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -420,7 +420,7 @@ cast_lossless = "warn" [workspace.dependencies] anyhow = "1.0" -anstyle = "1.0.11" +anstyle = "1.0.14" api_identity = { path = "api_identity" } approx = "0.5.1" assert_matches = "1.5.0" @@ -479,10 +479,10 @@ crossterm = { version = "0.29.0", features = ["event-stream"] } # NOTE: if you change the pinned revision of the `crucible` dependencies, you # must also update the references in package-manifest.toml to match the new # revision. -crucible-agent-client = { git = "https://github.com/oxidecomputer/crucible", rev = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" } -crucible-pantry-client = { git = "https://github.com/oxidecomputer/crucible", rev = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" } -crucible-smf = { git = "https://github.com/oxidecomputer/crucible", rev = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" } -crucible-common = { git = "https://github.com/oxidecomputer/crucible", rev = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" } +crucible-agent-client = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" } +crucible-pantry-client = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" } +crucible-smf = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" } +crucible-common = { git = "https://github.com/oxidecomputer/crucible", rev = "3c1708d86e10f0370807388a1efe092edd99d431" } # NOTE: See above! csv = "1.3.1" curve25519-dalek = "4" @@ -503,7 +503,7 @@ digest = "0.10.7" dns-server = { path = "dns-server" } dns-server-api = { path = "dns-server-api" } dns-service-client = { path = "clients/dns-service-client" } -dpd-client = { git = "https://github.com/oxidecomputer/dendrite", rev = "50ed531201ea5f85115141a2203d551fbbdaa3da" } +dpd-client = { git = "https://github.com/oxidecomputer/dendrite", rev = "90efdc6cf618523320bc403f1b9484dbd7f762be" } dropshot = { version = "0.17.0", features = [ "usdt-probes" ] } dropshot-api-manager = "0.7.2" dropshot-api-manager-types = "0.7.2" @@ -613,8 +613,9 @@ ntp-admin-client = { path = "clients/ntp-admin-client" } ntp-admin-v1-client = { path = "clients/ntp-admin-v1-client" } ntp-admin-types = { path = "ntp-admin/types" } ntp-admin-types-versions = { path = "ntp-admin/types/versions" } -mg-admin-client = { git = "https://github.com/oxidecomputer/maghemite", rev = "7696ee48d5ee29a917dea459e281fe2e8ff20513" } -ddm-admin-client = { git = "https://github.com/oxidecomputer/maghemite", rev = "7696ee48d5ee29a917dea459e281fe2e8ff20513" } +mg-admin-client = { git = "https://github.com/oxidecomputer/maghemite", rev = "5060d5864df864741dac8a0a4078fa4ecc3c73c8" } +mg-api-types = { git = "https://github.com/oxidecomputer/maghemite", rev = "5060d5864df864741dac8a0a4078fa4ecc3c73c8" } +ddm-admin-client = { git = "https://github.com/oxidecomputer/maghemite", rev = "5060d5864df864741dac8a0a4078fa4ecc3c73c8" } multimap = "0.10.1" nexus-auth = { path = "nexus/auth" } nexus-background-task-interface = { path = "nexus/background-task-interface" } @@ -678,7 +679,7 @@ oxide-client = { path = "clients/oxide-client" } oxide-tokio-rt = "0.1.4" oxide-vpc = { git = "https://github.com/oxidecomputer/opte", rev = "2c6efefe14321dafe7e9e80129d38316adb2d238", features = [ "api", "std" ] } oxlog = { path = "dev-tools/oxlog" } -oxnet = "0.1.4" +oxnet = "0.1.5" once_cell = "1.21.3" openapi-lint = { git = "https://github.com/oxidecomputer/openapi-lint", branch = "main" } openapiv3 = "2.2.0" @@ -726,21 +727,21 @@ proc-macro2 = "1.0" progenitor = "0.14.0" progenitor-client = "0.14.0" progenitor-extras = "0.2.0" -progenitor-client010 = { package = "progenitor-client", version = "0.10.0" } +progenitor-client013 = { package = "progenitor-client", version = "0.13.0" } # NOTE: if you change the pinned revision of the `bhyve_api` and propolis # dependencies, you must also update the references in package-manifest.toml to # match the new revision. -bhyve_api = { git = "https://github.com/oxidecomputer/propolis", rev = "bc489ddf0f38f75e0c194b86cf6f0de377f68845" } -propolis-api-types-versions = { git = "https://github.com/oxidecomputer/propolis", rev = "bc489ddf0f38f75e0c194b86cf6f0de377f68845" } -propolis_api_types = { git = "https://github.com/oxidecomputer/propolis", rev = "bc489ddf0f38f75e0c194b86cf6f0de377f68845" } -propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "bc489ddf0f38f75e0c194b86cf6f0de377f68845" } -propolis-mock-server = { git = "https://github.com/oxidecomputer/propolis", rev = "bc489ddf0f38f75e0c194b86cf6f0de377f68845" } +bhyve_api = { git = "https://github.com/oxidecomputer/propolis", rev = "75518ad2c26db9278895ab1262c28daf13bfdffd" } +propolis-api-types-versions = { git = "https://github.com/oxidecomputer/propolis", rev = "75518ad2c26db9278895ab1262c28daf13bfdffd" } +propolis_api_types = { git = "https://github.com/oxidecomputer/propolis", rev = "75518ad2c26db9278895ab1262c28daf13bfdffd" } +propolis-client = { git = "https://github.com/oxidecomputer/propolis", rev = "75518ad2c26db9278895ab1262c28daf13bfdffd" } +propolis-mock-server = { git = "https://github.com/oxidecomputer/propolis", rev = "75518ad2c26db9278895ab1262c28daf13bfdffd" } # NOTE: see above! proptest = "1.7.0" qorb = "0.4.1" quote = "1.0" # Some dependencies still require rand 0.8.x. -rand08 = { package = "rand", version = "0.8.6" } +rand08 = { package = "rand", version = "0.8.5" } rand = "0.9.2" rand_core = "0.9.3" rand_distr = "0.5.1" @@ -751,7 +752,6 @@ rats-corim = { git = "https://github.com/oxidecomputer/rats-corim.git", rev = "f raw-cpuid = { git = "https://github.com/oxidecomputer/rust-cpuid.git", rev = "a4cf01df76f35430ff5d39dc2fe470bcb953503b" } rayon = "1.10" rcgen = "0.12.1" -rdb-types = { git = "https://github.com/oxidecomputer/maghemite", rev = "7696ee48d5ee29a917dea459e281fe2e8ff20513" } reconfigurator-cli = { path = "dev-tools/reconfigurator-cli" } reedline = "0.40.0" ref-cast = "1.0" @@ -760,7 +760,6 @@ regress = "0.10.4" repo-depot-api = { path = "sled-agent/repo-depot-api" } repo-depot-client = { path = "clients/repo-depot-client" } reqwest = { version = "0.13", default-features = false } -reqwest012 = { package = "reqwest", version = "0.12", default-features = false } ring = "0.17.14" rpassword = "7.4.0" rstest = "0.25.0" @@ -1132,3 +1131,17 @@ path = "workspace-hack" [patch."https://github.com/oxidecomputer/omicron"] omicron-uuid-kinds = { path = "uuid-kinds" } omicron-common = { path = "common" } +# oxlog is built as a binary by omicron-package. Without this patch, +# transitive git-pulls of oxlog (illumos-utils -> oxlog, via maghemite's +# branch=main omicron deps) leave both local and git copies in the dep graph, +# and cargo's `--package oxlog` fails as ambiguous. +# +# TODO: This patch is a symptom of the broader maghemite -> omicron +# self-reference loop (maghemite depends on oximeter-producer, which pulls +# nexus-client -> nexus-types from omicron@main and not a rev). @jgallagher +# flagged this in review; @bnaecker suggested extracting oximeter-producer +# out of omicron to break it. When that lands, this patch (and any +# duplicated path+git entries for illumos-utils, nexus-types, etc.) can be +# removed. +oxlog = { path = "dev-tools/oxlog" } + diff --git a/clients/ddm-admin-client/src/lib.rs b/clients/ddm-admin-client/src/lib.rs index 7a8b56d499d..466a8883918 100644 --- a/clients/ddm-admin-client/src/lib.rs +++ b/clients/ddm-admin-client/src/lib.rs @@ -13,6 +13,7 @@ pub use ddm_admin_client::types; use ddm_admin_client::Client as InnerClient; use either::Either; +use omicron_common::address::DDMD_PORT; use oxnet::Ipv6Net; use sled_hardware_types::underlay::BOOTSTRAP_MASK; use sled_hardware_types::underlay::BOOTSTRAP_PREFIX; @@ -26,9 +27,6 @@ use thiserror::Error; use crate::types::EnableStatsRequest; -// TODO-cleanup Is it okay to hardcode this port number here? -const DDMD_PORT: u16 = 8000; - #[derive(Debug, Error, SlogInlineError)] pub enum DdmError { #[error("Failed to construct an HTTP client:")] diff --git a/common/Cargo.toml b/common/Cargo.toml index 3165f2a7599..5a2511374d6 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -52,7 +52,7 @@ tokio = { workspace = true, features = ["full"] } uuid.workspace = true parse-display.workspace = true progenitor-client.workspace = true -progenitor-client010.workspace = true +progenitor-client013.workspace = true progenitor-extras.workspace = true omicron-workspace-hack.workspace = true regress.workspace = true diff --git a/common/src/api/external/error.rs b/common/src/api/external/error.rs index 09f5b2f71c3..6538f976fe0 100644 --- a/common/src/api/external/error.rs +++ b/common/src/api/external/error.rs @@ -547,24 +547,23 @@ pub trait ClientError: std::fmt::Debug { // external client, others may require, for example, retries with an alternate // service instance or additional interpretation to sanitize the output error. // This should be removed to avoid leaking data. -impl From> for Error { - fn from(e: progenitor_client010::Error) -> Self { +impl From> for Error { + fn from(e: progenitor_client013::Error) -> Self { match e { // For most error variants, we delegate to the display impl for the // Progenitor error type, but we pick apart an error response more // carefully. - progenitor_client010::Error::InvalidRequest(_) - | progenitor_client010::Error::CommunicationError(_) - | progenitor_client010::Error::InvalidResponsePayload(..) - | progenitor_client010::Error::UnexpectedResponse(_) - | progenitor_client010::Error::InvalidUpgrade(_) - | progenitor_client010::Error::ResponseBodyError(_) - | progenitor_client010::Error::PreHookError(_) - | progenitor_client010::Error::PostHookError(_) => { + progenitor_client013::Error::InvalidRequest(_) + | progenitor_client013::Error::CommunicationError(_) + | progenitor_client013::Error::InvalidResponsePayload(..) + | progenitor_client013::Error::UnexpectedResponse(_) + | progenitor_client013::Error::InvalidUpgrade(_) + | progenitor_client013::Error::ResponseBodyError(_) + | progenitor_client013::Error::Custom(_) => { Error::internal_error(&e.to_string()) } // This error represents an expected error from the remote service. - progenitor_client010::Error::ErrorResponse(rv) => { + progenitor_client013::Error::ErrorResponse(rv) => { let message = rv.message(); match rv.status() { @@ -581,8 +580,9 @@ impl From> for Error { } } -// Equivalent From impl for progenitor-client 0.13. This coexists with the -// progenitor_client010 impl above during the cross-repo upgrade window. +// Equivalent From impl for progenitor-client 0.14 (workspace default). This +// coexists with the progenitor_client013 impl above during the cross-repo +// upgrade window. impl From> for Error { fn from(e: progenitor_client::Error) -> Self { match e { diff --git a/common/src/progenitor_operation_retry.rs b/common/src/progenitor_operation_retry.rs index 52a1dfb99ae..643900a9d34 100644 --- a/common/src/progenitor_operation_retry.rs +++ b/common/src/progenitor_operation_retry.rs @@ -25,14 +25,14 @@ pub enum ProgenitorOperationRetryError { /// The retry loop progenitor operation saw a permanent client error #[error("permanent error")] - ProgenitorError(#[source] progenitor_client010::Error), + ProgenitorError(#[source] progenitor_client013::Error), } impl ProgenitorOperationRetryError { pub fn is_not_found(&self) -> bool { match &self { ProgenitorOperationRetryError::ProgenitorError(e) => match e { - progenitor_client010::Error::ErrorResponse(rv) => { + progenitor_client013::Error::ErrorResponse(rv) => { match rv.status() { http::StatusCode::NOT_FOUND => true, @@ -70,7 +70,7 @@ pub struct ProgenitorOperationRetry< T, E: std::fmt::Debug, F: FnMut() -> Fut, - Fut: Future>>, + Fut: Future>>, BF: FnMut() -> BFut, BFut: Future>, > { @@ -85,7 +85,7 @@ impl ProgenitorOperationRetry where E: std::fmt::Debug + 'static, F: FnMut() -> Fut, - Fut: Future>>, + Fut: Future>>, BF: FnMut() -> BFut, BFut: Future>, { @@ -121,7 +121,7 @@ where } match f.await { - Err(progenitor_client010::Error::CommunicationError(e)) => { + Err(progenitor_client013::Error::CommunicationError(e)) => { warn!( log, "saw transient communication error, retrying..."; @@ -130,12 +130,12 @@ where Err(BackoffError::transient( ProgenitorOperationRetryError::ProgenitorError( - progenitor_client010::Error::CommunicationError(e) + progenitor_client013::Error::CommunicationError(e) ) )) } - Err(progenitor_client010::Error::ErrorResponse( + Err(progenitor_client013::Error::ErrorResponse( response_value, )) => { match response_value.status() { @@ -144,7 +144,7 @@ where | http::StatusCode::TOO_MANY_REQUESTS => { Err(BackoffError::transient( ProgenitorOperationRetryError::ProgenitorError( - progenitor_client010::Error::ErrorResponse( + progenitor_client013::Error::ErrorResponse( response_value ) ) @@ -154,7 +154,7 @@ where // Anything else is a permanent error _ => Err(BackoffError::Permanent( ProgenitorOperationRetryError::ProgenitorError( - progenitor_client010::Error::ErrorResponse( + progenitor_client013::Error::ErrorResponse( response_value ) ) diff --git a/dev-tools/downloader/src/lib.rs b/dev-tools/downloader/src/lib.rs index 44fb340de28..a7e5a65683b 100644 --- a/dev-tools/downloader/src/lib.rs +++ b/dev-tools/downloader/src/lib.rs @@ -69,6 +69,9 @@ enum Target { /// Maghemite mgd binary MaghemiteMgd, + /// Maghemite ddmd binary + MaghemiteDdmd, + /// SoftNPU, an admin program (scadm) and a pre-compiled P4 program. Softnpu, @@ -137,6 +140,7 @@ pub async fn run_cmd(args: DownloadArgs) -> Result<()> { Target::Console => downloader.download_console().await, Target::DendriteStub => downloader.download_dendrite_stub().await, Target::MaghemiteMgd => downloader.download_maghemite_mgd().await, + Target::MaghemiteDdmd => downloader.download_maghemite_ddmd().await, Target::Softnpu => downloader.download_softnpu().await, Target::TransceiverControl => { downloader.download_transceiver_control().await @@ -946,6 +950,84 @@ impl Downloader<'_> { Ok(()) } + async fn download_maghemite_ddmd(&self) -> Result<()> { + let download_dir = self.output_dir.join("downloads"); + tokio::fs::create_dir_all(&download_dir).await?; + + let checksums_path = self.versions_dir.join("maghemite_mgd_checksums"); + let [mg_ddm_sha2, ddmd_linux_sha2] = get_values_from_file( + ["MG_DDM_SHA256", "DDMD_LINUX_SHA256"], + &checksums_path, + ) + .await?; + let commit_path = + self.versions_dir.join("maghemite_ddm_openapi_version"); + let [commit] = get_values_from_file(["COMMIT"], &commit_path).await?; + + let repo = "oxidecomputer/maghemite"; + let base_url = format!("{BUILDOMAT_URL}/{repo}/image/{commit}"); + + let filename = "mg-ddm.tar.gz"; + let tarball_path = download_dir.join(filename); + download_file_and_verify( + &self.log, + &tarball_path, + &format!("{base_url}/{filename}"), + ChecksumAlgorithm::Sha2, + &mg_ddm_sha2, + ) + .await?; + unpack_tarball(&self.log, &tarball_path, &download_dir).await?; + + let destination_dir = self.output_dir.join("mg-ddm"); + let _ = tokio::fs::remove_dir_all(&destination_dir).await; + tokio::fs::create_dir_all(&destination_dir).await?; + copy_dir_all( + &download_dir.join("root"), + &destination_dir.join("root"), + )?; + + let binary_dir = destination_dir.join("root/opt/oxide/mg-ddm/bin"); + + match os_name()? { + Os::Linux => { + let filename = "ddmd"; + let path = download_dir.join(filename); + download_file_and_verify( + &self.log, + &path, + &format!( + "{BUILDOMAT_URL}/{repo}/linux/{commit}/{filename}" + ), + ChecksumAlgorithm::Sha2, + &ddmd_linux_sha2, + ) + .await?; + set_permissions(&path, 0o755).await?; + tokio::fs::copy(path, binary_dir.join(filename)).await?; + } + Os::Mac => { + info!( + self.log, + "Building maghemite ddmd from source for macOS" + ); + + let binaries = [("ddmd", &["--no-default-features"][..])]; + + let built_binaries = self + .build_from_git("maghemite", &commit, &binaries) + .await?; + + let dest = binary_dir.join("ddmd"); + tokio::fs::copy(&built_binaries[0], &dest).await?; + set_permissions(&dest, 0o755).await?; + } + Os::Illumos => (), + } + + Ok(()) + } + async fn download_softnpu(&self) -> Result<()> { let destination_dir = self.output_dir.join("npuzone"); tokio::fs::create_dir_all(&destination_dir).await?; diff --git a/dev-tools/ls-apis/api-manifest.toml b/dev-tools/ls-apis/api-manifest.toml index 7b8522aab74..966dfeb6a13 100644 --- a/dev-tools/ls-apis/api-manifest.toml +++ b/dev-tools/ls-apis/api-manifest.toml @@ -694,6 +694,15 @@ Sled Agent uses the Crucible Agent client types only, and only in the simulated sled agent. """ +[[dependency_filter_rules]] +ancestor = "omicron-sled-agent" +client = "crucible-pantry-client" +evaluation = "bogus" +note = """ +Sled Agent uses the Crucible Pantry client types only, and only in the simulated +sled agent. +""" + ################################################################################ # Intra-deployment-unit-only edges # diff --git a/dev-tools/omdb/Cargo.toml b/dev-tools/omdb/Cargo.toml index 777a715aa7d..7776bfa87c3 100644 --- a/dev-tools/omdb/Cargo.toml +++ b/dev-tools/omdb/Cargo.toml @@ -4,6 +4,10 @@ version = "0.1.0" edition.workspace = true license = "MPL-2.0" +[features] +default = ["multicast"] +multicast = [] + [lints] workspace = true diff --git a/dev-tools/omdb/src/bin/omdb/crucible_pantry.rs b/dev-tools/omdb/src/bin/omdb/crucible_pantry.rs index 3751d934488..043f9d1c8c9 100644 --- a/dev-tools/omdb/src/bin/omdb/crucible_pantry.rs +++ b/dev-tools/omdb/src/bin/omdb/crucible_pantry.rs @@ -101,12 +101,54 @@ async fn cmd_volume_info( args: &VolumeArgs, ) -> Result<(), anyhow::Error> { let volume = args.uuid.to_string(); - let VolumeStatus { active, num_job_handles, seen_active } = - *client.volume_status(&volume).await.context("listing volumes")?; - - println!(" active: {}", active); - println!(" num_job_handles: {}", num_job_handles); - println!(" seen_active: {}", seen_active); + let VolumeStatus { active, num_job_handles, seen_active, info } = client + .volume_status(&volume) + .await + .context("listing volumes")? + .into_inner(); + + println!(" active: {active}"); + println!(" num_job_handles: {num_job_handles}"); + println!(" seen_active: {seen_active}"); + + match info { + crucible_pantry_client::types::VolumeInfo::Volume { + sub_volumes, + read_only_parent, + } => { + println!(" info: volume"); + println!(" sub_volumes: {}", sub_volumes.len()); + println!("read_only_parent: {}", read_only_parent.is_some()); + } + crucible_pantry_client::types::VolumeInfo::Upstairs { + state, + upstairs_id, + session_id, + generation, + live_repair_in_progress, + reconcile_in_progress, + block_size, + encrypted, + read_only, + targets, + .. + } => { + println!(" info: upstairs"); + println!(" upstairs_id: {upstairs_id}"); + println!(" session_id: {session_id}"); + println!(" generation: {generation}"); + println!(" state: {state:?}"); + println!(" block_size: {block_size:?}"); + println!(" encrypted: {encrypted}"); + println!(" read_only: {read_only}"); + println!(" live_repair_in_progress: {live_repair_in_progress}"); + println!(" reconcile_in_progress: {reconcile_in_progress}"); + println!(" targets ({}):", targets.len()); + for (i, t) in targets.iter().enumerate() { + println!(" [{i}] {t:?}"); + } + } + } Ok(()) } diff --git a/dev-tools/omdb/src/bin/omdb/db.rs b/dev-tools/omdb/src/bin/omdb/db.rs index 22d553fbdcc..3b19dcab4c2 100644 --- a/dev-tools/omdb/src/bin/omdb/db.rs +++ b/dev-tools/omdb/src/bin/omdb/db.rs @@ -188,6 +188,7 @@ mod alert; mod blueprints; mod db_metadata; mod ereport; +mod multicast; mod saga; mod sitrep; mod user_data_export; @@ -414,6 +415,8 @@ enum DbCommands { /// Print information about migrations #[clap(alias = "migration")] Migrations(MigrationsArgs), + /// Print information about multicast groups + Multicast(multicast::MulticastArgs), /// Print information about snapshots Snapshots(SnapshotArgs), /// Validate the contents of the database @@ -1497,6 +1500,33 @@ impl DbArgs { }) => { cmd_db_migrations_list(&datastore, &fetch_opts, args).await } + DbCommands::Multicast(multicast::MulticastArgs { + command: multicast::MulticastCommands::Groups(args), + }) => { + multicast::cmd_db_multicast_groups( + &datastore, &fetch_opts, &args, + ) + .await + } + DbCommands::Multicast(multicast::MulticastArgs { + command: multicast::MulticastCommands::Members(args), + }) => { + multicast::cmd_db_multicast_members(&datastore, &fetch_opts, &args) + .await + } + DbCommands::Multicast(multicast::MulticastArgs { + command: multicast::MulticastCommands::Pools, + }) => { + multicast::cmd_db_multicast_pools(&datastore, &fetch_opts).await + } + DbCommands::Multicast(multicast::MulticastArgs { + command: multicast::MulticastCommands::Info(args), + }) => { + multicast::cmd_db_multicast_info( + &datastore, &fetch_opts, &args, + ) + .await + } DbCommands::Snapshots(SnapshotArgs { command: SnapshotCommands::Info(uuid), }) => cmd_db_snapshot_info(&opctx, &datastore, uuid).await, diff --git a/dev-tools/omdb/src/bin/omdb/db/multicast.rs b/dev-tools/omdb/src/bin/omdb/db/multicast.rs new file mode 100644 index 00000000000..68118b2ca80 --- /dev/null +++ b/dev-tools/omdb/src/bin/omdb/db/multicast.rs @@ -0,0 +1,940 @@ +// 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/. + +//! `omdb db multicast` subcommands +//! +//! # Command Outputs +//! +//! ## `omdb db multicast pools` +//! +//! | Column | Description | +//! |---------------|----------------------------| +//! | POOL_ID | Pool UUID | +//! | POOL_NAME | Pool name | +//! | FIRST_ADDRESS | Range start IP | +//! | LAST_ADDRESS | Range end IP | +//! | CREATED | Creation timestamp | +//! +//! ## `omdb db multicast groups` +//! +//! | Column | Description | +//! |--------------|--------------------------------------| +//! | ID | Group UUID | +//! | NAME | Group name | +//! | STATE | Group state ("Active"/"Creating") | +//! | MULTICAST_IP | Allocated multicast IP | +//! | RANGE | ASM or SSM based on IP range | +//! | UNDERLAY_IP | Underlay group IP (blank if none) | +//! | SOURCES | Source allowlist, or "-" (any) | +//! | MEMBERS | Comma-separated "instance@sled" list | +//! | VNI | Virtual network ID | +//! | CREATED | Creation timestamp | +//! +//! Note: SOURCES "-" also appears when a group has no members; use MEMBERS +//! column or `info` command to distinguish from any-source members. +//! +//! Filters: `--state`, `--pool` +//! +//! ## `omdb db multicast members` +//! +//! | Column | Description | +//! |--------------|------------------------------------------| +//! | ID | Member UUID | +//! | GROUP_NAME | Parent group name | +//! | PARENT_ID | Instance UUID | +//! | STATE | Member state ("Joining"/"Joined"/"Left") | +//! | MULTICAST_IP | Group multicast IP | +//! | SOURCES | Source allowlist, or "-" (any) | +//! | SLED_ID | Assigned sled UUID (blank if none) | +//! | CREATED | Creation timestamp | +//! +//! Filters: `--group-id`, `--group-ip`, `--group-name`, `--state`, +//! `--sled-id`, `--source-ip` +//! +//! ## `omdb db multicast info` +//! +//! Detailed view of a single group with sections: +//! +//! **MULTICAST GROUP** +//! - id, name, state, multicast_ip, vni, source_ips (allowlist or "-") +//! - ip_pool (name + ID), underlay_group, tag, created +//! +//! **UNDERLAY GROUP** (if present) +//! - id, multicast_ip, tag, created +//! +//! **MEMBERS** (table) +//! +//! | Column | Description | +//! |--------------|--------------------------------| +//! | ID | Member UUID | +//! | INSTANCE | Instance name | +//! | STATE | Member state | +//! | MULTICAST_IP | Group multicast IP | +//! | SOURCES | Allowlist, or "-" (any) | +//! | SLED | Sled serial number, or "-" | +//! | CREATED | Creation timestamp | +//! +//! Lookup: `--group-id`, `--ip`, `--name` (exactly one required) + +use std::collections::{BTreeSet, HashMap}; + +use anyhow::Context; +use async_bb8_diesel::AsyncRunQueryDsl; +use chrono::{DateTime, Utc}; +use clap::{Args, Subcommand, ValueEnum}; +use diesel::prelude::*; +use tabled::Tabled; +use uuid::Uuid; + +use nexus_db_model::{ + ExternalMulticastGroup, IpPool, IpPoolRange, IpPoolType, + MulticastGroupMember, MulticastGroupMemberState, MulticastGroupState, + UnderlayMulticastGroup, +}; +use nexus_db_queries::db::DataStore; +use nexus_types::identity::Resource; +use omicron_common::address::is_ssm_address; +use omicron_uuid_kinds::{GenericUuid, SledUuid}; + +/// CLI wrapper for MulticastGroupState to support clap ValueEnum +#[derive(Debug, Clone, Copy, ValueEnum)] +pub enum CliGroupState { + Creating, + Active, + Deleting, + Deleted, +} + +impl From for MulticastGroupState { + fn from(cli: CliGroupState) -> Self { + match cli { + CliGroupState::Creating => MulticastGroupState::Creating, + CliGroupState::Active => MulticastGroupState::Active, + CliGroupState::Deleting => MulticastGroupState::Deleting, + CliGroupState::Deleted => MulticastGroupState::Deleted, + } + } +} + +/// CLI wrapper for MulticastGroupMemberState to support clap ValueEnum +#[derive(Debug, Clone, Copy, ValueEnum)] +pub enum CliMemberState { + Joining, + Joined, + Left, +} + +impl From for MulticastGroupMemberState { + fn from(cli: CliMemberState) -> Self { + match cli { + CliMemberState::Joining => MulticastGroupMemberState::Joining, + CliMemberState::Joined => MulticastGroupMemberState::Joined, + CliMemberState::Left => MulticastGroupMemberState::Left, + } + } +} + +use crate::db::{DbFetchOptions, check_limit}; +use crate::helpers::{datetime_rfc3339_concise, display_option_blank}; + +// Display labels for multicast address range classification +const RANGE_SSM: &str = "SSM"; +const RANGE_ASM: &str = "ASM"; + +/// `omdb db multicast` subcommand +#[derive(Debug, Args, Clone)] +pub(super) struct MulticastArgs { + #[command(subcommand)] + pub command: MulticastCommands, +} + +#[derive(Debug, Subcommand, Clone)] +pub(super) enum MulticastCommands { + /// List all multicast groups. + /// + /// Shows ID, name, state, multicast IP, address range type (ASM/SSM), + /// underlay IP, source IPs union, VNI, and creation time. + #[clap(alias = "ls")] + Groups(MulticastGroupsArgs), + + /// List all multicast group members. + /// + /// Shows member ID, group name, parent instance ID, state, multicast IP, + /// source IPs, sled ID, and creation time. + Members(MulticastMembersArgs), + + /// List multicast IP pools and their ranges. + /// + /// Shows pool ID, name, first/last addresses, and creation time. + Pools, + + /// Get detailed info for a multicast group. + /// + /// Shows group details, associated underlay group, and all members. + #[clap(alias = "show")] + Info(MulticastInfoArgs), +} + +#[derive(Debug, Args, Clone)] +pub(super) struct MulticastGroupsArgs { + /// Filter by state + #[arg(long, ignore_case = true, value_enum)] + state: Option, + /// Filter by pool name + #[arg(long)] + pool: Option, +} + +#[derive(Debug, Args, Clone)] +pub(super) struct MulticastMembersArgs { + /// Filter by group ID + #[arg(long)] + group_id: Option, + /// Filter by group IP address (e.g., 239.1.2.3) + #[arg(long)] + group_ip: Option, + /// Filter by group name + #[arg(long)] + group_name: Option, + /// Filter by state + #[arg(long, ignore_case = true, value_enum)] + state: Option, + /// Filter by sled ID + #[arg(long)] + sled_id: Option, + /// Filter by source IP (members subscribed to this source) + #[arg(long)] + source_ip: Option, +} + +#[derive(Debug, Args, Clone)] +#[group(required = true, multiple = false)] +pub(super) struct MulticastInfoArgs { + /// Multicast group ID + #[arg(long)] + group_id: Option, + /// Multicast IP address (e.g., 239.1.2.3) + #[arg(long)] + ip: Option, + /// Multicast group name + #[arg(long)] + name: Option, +} + +#[derive(Tabled)] +#[tabled(rename_all = "SCREAMING_SNAKE_CASE")] +struct MulticastGroupRow { + id: Uuid, + name: String, + state: MulticastGroupState, + multicast_ip: std::net::IpAddr, + /// ASM (any-source) or SSM (source-specific) based on IP range + range: &'static str, + #[tabled(display_with = "display_option_blank")] + underlay_ip: Option, + /// Source IPs union from members ("-" = any source) + sources: String, + /// Members formatted as "inst_1@sled, ..., inst_n@sled" + members: String, + vni: u32, + #[tabled(display_with = "datetime_rfc3339_concise")] + created: DateTime, +} + +#[derive(Tabled)] +#[tabled(rename_all = "SCREAMING_SNAKE_CASE")] +struct MulticastMemberRow { + id: Uuid, + group_name: String, + parent_id: Uuid, + state: MulticastGroupMemberState, + multicast_ip: std::net::IpAddr, + /// Source IPs for source filtering ("-" = any source) + sources: String, + #[tabled(display_with = "display_option_blank")] + sled_id: Option, + #[tabled(display_with = "datetime_rfc3339_concise")] + created: DateTime, +} + +#[derive(Tabled)] +#[tabled(rename_all = "SCREAMING_SNAKE_CASE")] +struct MulticastInfoMemberRow { + id: Uuid, + instance: String, + state: MulticastGroupMemberState, + multicast_ip: std::net::IpAddr, + /// Source IPs for source filtering ("-" = any source) + sources: String, + sled: String, + #[tabled(display_with = "datetime_rfc3339_concise")] + created: DateTime, +} + +// Build output combining pools and ranges +#[derive(Tabled)] +#[tabled(rename_all = "SCREAMING_SNAKE_CASE")] +struct MulticastPoolRow { + pool_id: Uuid, + pool_name: String, + first_address: std::net::IpAddr, + last_address: std::net::IpAddr, + #[tabled(display_with = "datetime_rfc3339_concise")] + created: chrono::DateTime, +} + +pub(super) async fn cmd_db_multicast_groups( + datastore: &DataStore, + fetch_opts: &DbFetchOptions, + args: &MulticastGroupsArgs, +) -> Result<(), anyhow::Error> { + use nexus_db_schema::schema::instance::dsl as instance_dsl; + use nexus_db_schema::schema::ip_pool::dsl as pool_dsl; + use nexus_db_schema::schema::multicast_group::dsl; + use nexus_db_schema::schema::multicast_group_member::dsl as member_dsl; + use nexus_db_schema::schema::sled::dsl as sled_dsl; + use nexus_db_schema::schema::underlay_multicast_group::dsl as underlay_dsl; + + let conn = datastore.pool_connection_for_tests().await?; + + let mut query = dsl::multicast_group.into_boxed(); + if !fetch_opts.include_deleted { + query = query.filter(dsl::time_deleted.is_null()); + } + if let Some(state) = args.state.map(MulticastGroupState::from) { + query = query.filter(dsl::state.eq(state)); + } + if let Some(ref pool_name) = args.pool { + let pool_id: Uuid = pool_dsl::ip_pool + .filter(pool_dsl::name.eq(pool_name.clone())) + .filter(pool_dsl::time_deleted.is_null()) + .select(pool_dsl::id) + .first_async(&*conn) + .await + .with_context(|| { + format!("no pool found with name '{pool_name}'") + })?; + query = query.filter(dsl::ip_pool_id.eq(pool_id)); + } + + let groups: Vec = query + .order_by(dsl::time_created.desc()) + .limit(i64::from(u32::from(fetch_opts.fetch_limit))) + .select(ExternalMulticastGroup::as_select()) + .get_results_async(&*conn) + .await?; + + check_limit(&groups, fetch_opts.fetch_limit, || { + String::from("listing multicast groups") + }); + + // Batch lookup underlay IPs for groups + let underlay_ids: Vec = + groups.iter().filter_map(|group| group.underlay_group_id).collect(); + let underlay_map: HashMap = + if underlay_ids.is_empty() { + HashMap::new() + } else { + underlay_dsl::underlay_multicast_group + .filter(underlay_dsl::id.eq_any(underlay_ids)) + .select((underlay_dsl::id, underlay_dsl::multicast_ip)) + .get_results_async::<(Uuid, ipnetwork::IpNetwork)>(&*conn) + .await + .context("fetching underlay groups")? + .into_iter() + .collect() + }; + + // Fetch members for all groups with deterministic ordering + let group_ids: Vec = groups.iter().map(|g| g.identity.id).collect(); + let members: Vec = if group_ids.is_empty() { + Vec::new() + } else { + let mut mq = member_dsl::multicast_group_member + .filter(member_dsl::external_group_id.eq_any(group_ids)) + .into_boxed(); + if !fetch_opts.include_deleted { + mq = mq.filter(member_dsl::time_deleted.is_null()); + } + mq.order_by(member_dsl::external_group_id.asc()) + .then_order_by(member_dsl::parent_id.asc()) + .then_order_by(member_dsl::id.asc()) + .select(MulticastGroupMember::as_select()) + .get_results_async(&*conn) + .await + .context("fetching multicast group members")? + }; + + // Derive effective source filtering state from members for each group. + // If any member has empty source_ips, the group effectively allows any source + // (filter disabled). Otherwise, show the union of all member source IPs + // (filter enabled with allowlist). None = any-source, Some(set) = filtered. + let source_filter_map: HashMap>> = { + let mut map: HashMap>> = + HashMap::new(); + for member in &members { + if member.source_ips.is_empty() { + // Any member with empty sources means any-source allowed + map.insert(member.external_group_id, None); + } else { + map.entry(member.external_group_id) + .and_modify(|v| { + if let Some(set) = v { + for ip in &member.source_ips { + set.insert(ip.ip()); + } + } + // If already None (any-source), leave it as None + }) + .or_insert_with(|| { + Some( + member + .source_ips + .iter() + .map(|ip| ip.ip()) + .collect(), + ) + }); + } + } + map + }; + + // Batch lookup instance names + let parent_ids: Vec = members.iter().map(|m| m.parent_id).collect(); + let instance_names: HashMap = if parent_ids.is_empty() { + HashMap::new() + } else { + instance_dsl::instance + .filter(instance_dsl::id.eq_any(parent_ids)) + .select((instance_dsl::id, instance_dsl::name)) + .get_results_async::<(Uuid, String)>(&*conn) + .await + .context("fetching instance names")? + .into_iter() + .collect() + }; + + // Batch lookup sled serials + let sled_ids: Vec = members + .iter() + .filter_map(|m| m.sled_id.map(|s| s.into_untyped_uuid())) + .collect(); + let sled_serials: HashMap = if sled_ids.is_empty() { + HashMap::new() + } else { + sled_dsl::sled + .filter(sled_dsl::id.eq_any(sled_ids)) + .select((sled_dsl::id, sled_dsl::serial_number)) + .get_results_async::<(Uuid, String)>(&*conn) + .await + .context("fetching sled serials")? + .into_iter() + .collect() + }; + + // Build group_id -> formatted members string + let mut members_map: HashMap> = HashMap::new(); + for member in &members { + let inst_name = instance_names + .get(&member.parent_id) + .cloned() + .unwrap_or_else(|| member.parent_id.to_string()); + let sled_serial = member + .sled_id + .and_then(|s| sled_serials.get(&s.into_untyped_uuid()).cloned()) + .unwrap_or_else(|| "-".to_string()); + let formatted = format!("{inst_name}@{sled_serial}"); + members_map + .entry(member.external_group_id) + .or_default() + .push(formatted); + } + + // Sort each group's members for deterministic output + for v in members_map.values_mut() { + v.sort_unstable(); + } + + let rows: Vec = groups + .into_iter() + .map(|group| { + let mcast_ip = group.multicast_ip.ip(); + let range = + if is_ssm_address(mcast_ip) { RANGE_SSM } else { RANGE_ASM }; + // Format effective source filter state (derived from members) + let sources = source_filter_map + .get(&group.identity.id) + .and_then(|opt| opt.as_ref()) + .filter(|set| !set.is_empty()) + .map(|set| { + set.iter() + .map(|ip| ip.to_string()) + .collect::>() + .join(",") + }) + .unwrap_or_else(|| "-".to_string()); + let underlay_ip = group + .underlay_group_id + .and_then(|id| underlay_map.get(&id)) + .map(|ip| ip.ip()); + let members = members_map + .get(&group.identity.id) + .map(|v| v.join(", ")) + .unwrap_or_else(|| "-".to_string()); + MulticastGroupRow { + id: group.identity.id, + name: group.identity.name.to_string(), + state: group.state, + multicast_ip: mcast_ip, + range, + underlay_ip, + sources, + members, + vni: u32::from(group.vni.0), + created: group.identity.time_created, + } + }) + .collect(); + + let table = tabled::Table::new(rows) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("{table}"); + + Ok(()) +} + +pub(super) async fn cmd_db_multicast_members( + datastore: &DataStore, + fetch_opts: &DbFetchOptions, + args: &MulticastMembersArgs, +) -> Result<(), anyhow::Error> { + use nexus_db_schema::schema::multicast_group::dsl as group_dsl; + use nexus_db_schema::schema::multicast_group_member::dsl; + + let conn = datastore.pool_connection_for_tests().await?; + + // Resolve group_ip or group_name to a group_id if specified + let resolved_group_id = match (&args.group_ip, &args.group_name) { + (Some(ip), _) => { + let group: ExternalMulticastGroup = group_dsl::multicast_group + .filter(group_dsl::time_deleted.is_null()) + .filter( + group_dsl::multicast_ip.eq(ipnetwork::IpNetwork::from(*ip)), + ) + .select(ExternalMulticastGroup::as_select()) + .first_async(&*conn) + .await + .with_context(|| format!("no multicast group with IP {ip}"))?; + Some(group.id()) + } + (None, Some(name)) => { + let group: ExternalMulticastGroup = group_dsl::multicast_group + .filter(group_dsl::time_deleted.is_null()) + .filter(group_dsl::name.eq(name.clone())) + .select(ExternalMulticastGroup::as_select()) + .first_async(&*conn) + .await + .with_context(|| { + format!("no multicast group with name '{name}'") + })?; + Some(group.id()) + } + (None, None) => args.group_id, + }; + + let mut query = dsl::multicast_group_member.into_boxed(); + if !fetch_opts.include_deleted { + query = query.filter(dsl::time_deleted.is_null()); + } + if let Some(group_id) = resolved_group_id { + query = query.filter(dsl::external_group_id.eq(group_id)); + } + if let Some(state) = args.state.map(MulticastGroupMemberState::from) { + query = query.filter(dsl::state.eq(state)); + } + if let Some(sled_id) = args.sled_id { + query = query.filter(dsl::sled_id.eq(sled_id.into_untyped_uuid())); + } + if let Some(source_ip) = args.source_ip { + let ip_network = ipnetwork::IpNetwork::from(source_ip); + query = query.filter(dsl::source_ips.contains(vec![ip_network])); + } + + let members: Vec = query + .order_by(dsl::time_created.desc()) + .limit(i64::from(u32::from(fetch_opts.fetch_limit))) + .select(MulticastGroupMember::as_select()) + .get_results_async(&*conn) + .await?; + + check_limit(&members, fetch_opts.fetch_limit, || { + String::from("listing multicast group members") + }); + + // Batch lookup group names + let group_ids: Vec = + members.iter().map(|member| member.external_group_id).collect(); + let group_names: HashMap = if group_ids.is_empty() { + HashMap::new() + } else { + group_dsl::multicast_group + .filter(group_dsl::id.eq_any(group_ids)) + .select((group_dsl::id, group_dsl::name)) + .get_results_async::<(Uuid, String)>(&*conn) + .await + .context("fetching group names")? + .into_iter() + .collect() + }; + + let rows: Vec = members + .into_iter() + .map(|member| { + let group_name = group_names + .get(&member.external_group_id) + .cloned() + .unwrap_or_else(|| member.external_group_id.to_string()); + let sources = Some(&member.source_ips) + .filter(|ips| !ips.is_empty()) + .map(|ips| { + ips.iter() + .map(|ip| ip.ip().to_string()) + .collect::>() + .join(",") + }) + .unwrap_or_else(|| "-".to_string()); + MulticastMemberRow { + id: member.id, + group_name, + parent_id: member.parent_id, + state: member.state, + multicast_ip: member.multicast_ip.ip(), + sources, + sled_id: member.sled_id.map(SledUuid::from), + created: member.time_created, + } + }) + .collect(); + + let table = tabled::Table::new(rows) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("{table}"); + + Ok(()) +} + +pub(super) async fn cmd_db_multicast_pools( + datastore: &DataStore, + fetch_opts: &DbFetchOptions, +) -> Result<(), anyhow::Error> { + use nexus_db_schema::schema::ip_pool::dsl as pool_dsl; + use nexus_db_schema::schema::ip_pool_range::dsl as range_dsl; + + let conn = datastore.pool_connection_for_tests().await?; + + // Get multicast pools + let mut query = pool_dsl::ip_pool.into_boxed(); + query = query.filter(pool_dsl::pool_type.eq(IpPoolType::Multicast)); + if !fetch_opts.include_deleted { + query = query.filter(pool_dsl::time_deleted.is_null()); + } + + let pools: Vec = query + .order_by(pool_dsl::time_created.desc()) + .limit(i64::from(u32::from(fetch_opts.fetch_limit))) + .select(IpPool::as_select()) + .get_results_async(&*conn) + .await?; + + check_limit(&pools, fetch_opts.fetch_limit, || { + String::from("listing multicast pools") + }); + + if pools.is_empty() { + println!("no multicast IP pools found"); + return Ok(()); + } + + // Get ranges for each pool + let pool_ids: Vec = pools.iter().map(|pool| pool.id()).collect(); + + let mut range_query = range_dsl::ip_pool_range.into_boxed(); + range_query = + range_query.filter(range_dsl::ip_pool_id.eq_any(pool_ids.clone())); + if !fetch_opts.include_deleted { + range_query = range_query.filter(range_dsl::time_deleted.is_null()); + } + + let ranges: Vec = range_query + .order_by(range_dsl::first_address) + .select(IpPoolRange::as_select()) + .get_results_async(&*conn) + .await?; + + let pool_map: HashMap = + pools.iter().map(|pool| (pool.id(), pool)).collect(); + + let rows: Vec = ranges + .into_iter() + .filter_map(|range| { + pool_map.get(&range.ip_pool_id).map(|pool| MulticastPoolRow { + pool_id: pool.id(), + pool_name: pool.name().to_string(), + first_address: range.first_address.ip(), + last_address: range.last_address.ip(), + created: range.time_created, + }) + }) + .collect(); + + if rows.is_empty() { + println!("no multicast IP pool ranges found"); + return Ok(()); + } + + let table = tabled::Table::new(rows) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("{table}"); + + Ok(()) +} + +pub(super) async fn cmd_db_multicast_info( + datastore: &DataStore, + fetch_opts: &DbFetchOptions, + args: &MulticastInfoArgs, +) -> Result<(), anyhow::Error> { + use nexus_db_schema::schema::instance::dsl as instance_dsl; + use nexus_db_schema::schema::ip_pool::dsl as pool_dsl; + use nexus_db_schema::schema::multicast_group::dsl as group_dsl; + use nexus_db_schema::schema::multicast_group_member::dsl as member_dsl; + use nexus_db_schema::schema::sled::dsl as sled_dsl; + use nexus_db_schema::schema::underlay_multicast_group::dsl as underlay_dsl; + + let conn = datastore.pool_connection_for_tests().await?; + + // Find the group by ID, IP, or name, pairing filter with error message + let (mut query, not_found_msg) = + match (&args.group_id, &args.ip, &args.name) { + (Some(id), _, _) => ( + group_dsl::multicast_group + .filter(group_dsl::id.eq(*id)) + .into_boxed(), + format!("no multicast group found with ID {id}"), + ), + (None, Some(ip), _) => ( + group_dsl::multicast_group + .filter( + group_dsl::multicast_ip + .eq(ipnetwork::IpNetwork::from(*ip)), + ) + .into_boxed(), + format!("no multicast group found with IP {ip}"), + ), + (None, None, Some(name)) => ( + group_dsl::multicast_group + .filter(group_dsl::name.eq(name.clone())) + .into_boxed(), + format!("no multicast group found with name \"{name}\""), + ), + (None, None, None) => { + anyhow::bail!("must specify --group-id, --ip, or --name") + } + }; + + if !fetch_opts.include_deleted { + query = query.filter(group_dsl::time_deleted.is_null()); + } + + // Fetch group with underlay in single query using LEFT JOIN + let result: Option<( + ExternalMulticastGroup, + Option, + )> = + query + .left_join(underlay_dsl::underlay_multicast_group.on( + underlay_dsl::id.nullable().eq(group_dsl::underlay_group_id), + )) + .select(( + ExternalMulticastGroup::as_select(), + Option::::as_select(), + )) + .first_async(&*conn) + .await + .optional()?; + + let (group, underlay) = match result { + Some((grp, ulay)) => (grp, ulay), + None => { + println!("{not_found_msg}"); + return Ok(()); + } + }; + + // Look up the pool name + let pool_name: String = match pool_dsl::ip_pool + .filter(pool_dsl::id.eq(group.ip_pool_id)) + .select(pool_dsl::name) + .first_async(&*conn) + .await + .optional() + .context("fetching pool name")? + { + Some(name) => name, + None => { + eprintln!("warning: no pool found for id {}", group.ip_pool_id); + "".into() + } + }; + + // Find members for this group (fetch early to derive source_ips) + let mut member_query = member_dsl::multicast_group_member.into_boxed(); + member_query = member_query + .filter(member_dsl::external_group_id.eq(group.identity.id)); + if !fetch_opts.include_deleted { + member_query = member_query.filter(member_dsl::time_deleted.is_null()); + } + + let members: Vec = member_query + .order_by(member_dsl::time_created.desc()) + .select(MulticastGroupMember::as_select()) + .get_results_async(&*conn) + .await?; + + // Derive effective source filter state from members. + // If any member has empty source_ips, the group allows any source ("-"). + // Otherwise, show the union of all member source IPs. + let has_any_source_member = members.iter().any(|m| m.source_ips.is_empty()); + let source_ips_display = if has_any_source_member { + "-".to_string() + } else { + let source_ips: BTreeSet = members + .iter() + .flat_map(|m| m.source_ips.iter().map(|ip| ip.ip())) + .collect(); + if source_ips.is_empty() { + "-".to_string() + } else { + source_ips + .iter() + .map(|ip| ip.to_string()) + .collect::>() + .join(",") + } + }; + + // Print group details + println!("MULTICAST GROUP"); + println!(" id: {}", group.identity.id); + println!(" name: {}", group.identity.name); + println!(" state: {:?}", group.state); + println!(" multicast_ip: {}", group.multicast_ip); + println!(" vni: {}", u32::from(group.vni.0)); + println!(" source_ips: {source_ips_display}"); + println!(" ip_pool: {pool_name} ({})", group.ip_pool_id); + println!(" underlay_group: {:?}", group.underlay_group_id); + println!(" tag: {:?}", group.tag); + println!(" created: {}", group.identity.time_created); + if let Some(deleted) = group.identity.time_deleted { + println!(" deleted: {deleted}"); + } + + // Display underlay group if present + if let Some(underlay_group) = underlay { + println!("\nUNDERLAY GROUP"); + println!(" id: {}", underlay_group.id); + println!(" multicast_ip: {}", underlay_group.multicast_ip); + println!(" tag: {:?}", underlay_group.tag); + println!(" created: {}", underlay_group.time_created); + if let Some(deleted) = underlay_group.time_deleted { + println!(" deleted: {deleted}"); + } + } + + if members.is_empty() { + println!("\nMEMBERS: (none)"); + } else { + println!("\nMEMBERS ({}):", members.len()); + + // Batch lookup instance names (parent_id references instances) + let parent_ids: Vec = + members.iter().map(|member| member.parent_id).collect(); + let instances: Vec<(Uuid, String)> = instance_dsl::instance + .filter(instance_dsl::id.eq_any(parent_ids)) + .select((instance_dsl::id, instance_dsl::name)) + .get_results_async(&*conn) + .await + .context("fetching instance names")?; + let instance_map: HashMap = + instances.into_iter().collect(); + + // Batch lookup sled serials + let sled_ids: Vec = members + .iter() + .filter_map(|member| member.sled_id.map(|s| s.into_untyped_uuid())) + .collect(); + let sleds: Vec<(Uuid, String)> = if sled_ids.is_empty() { + Vec::new() + } else { + sled_dsl::sled + .filter(sled_dsl::id.eq_any(sled_ids)) + .select((sled_dsl::id, sled_dsl::serial_number)) + .get_results_async(&*conn) + .await + .context("fetching sled serials")? + }; + let sled_map: HashMap = sleds.into_iter().collect(); + + let rows: Vec = members + .into_iter() + .map(|member| { + let instance_name = instance_map + .get(&member.parent_id) + .cloned() + .unwrap_or_else(|| member.parent_id.to_string()); + let sled_serial = member + .sled_id + .and_then(|s| sled_map.get(&s.into_untyped_uuid()).cloned()) + .unwrap_or_else(|| "-".to_string()); + let sources = Some(&member.source_ips) + .filter(|ips| !ips.is_empty()) + .map(|ips| { + ips.iter() + .map(|ip| ip.ip().to_string()) + .collect::>() + .join(",") + }) + .unwrap_or_else(|| "-".to_string()); + MulticastInfoMemberRow { + id: member.id, + instance: instance_name, + state: member.state, + multicast_ip: member.multicast_ip.ip(), + sources, + sled: sled_serial, + created: member.time_created, + } + }) + .collect(); + + let table = tabled::Table::new(rows) + .with(tabled::settings::Style::empty()) + .with(tabled::settings::Padding::new(0, 1, 0, 0)) + .to_string(); + + println!("{table}"); + } + + Ok(()) +} diff --git a/dev-tools/omdb/src/bin/omdb/nexus.rs b/dev-tools/omdb/src/bin/omdb/nexus.rs index 7496a6255fa..0bd2f1d4fcc 100644 --- a/dev-tools/omdb/src/bin/omdb/nexus.rs +++ b/dev-tools/omdb/src/bin/omdb/nexus.rs @@ -65,6 +65,7 @@ use nexus_types::internal_api::background::InstanceReincarnationStatus; use nexus_types::internal_api::background::InstanceUpdaterStatus; use nexus_types::internal_api::background::InventoryLoadStatus; use nexus_types::internal_api::background::LookupRegionPortStatus; +use nexus_types::internal_api::background::MulticastGroupReconcilerStatus; use nexus_types::internal_api::background::PhysicalDiskAdoptionStatus; use nexus_types::internal_api::background::ProbeDistributorStatus; use nexus_types::internal_api::background::ReadOnlyRegionReplacementStartStatus; @@ -1300,6 +1301,9 @@ fn print_task_details(bgtask: &BackgroundTask, details: &serde_json::Value) { "lookup_region_port" => { print_task_lookup_region_port(details); } + "multicast_reconciler" => { + print_task_multicast_reconciler(details); + } "phantom_disks" => { print_task_phantom_disks(details); } @@ -2246,6 +2250,76 @@ fn print_task_lookup_region_port(details: &serde_json::Value) { } } +fn print_task_multicast_reconciler(details: &serde_json::Value) { + let status = match serde_json::from_value::( + details.clone(), + ) { + Err(error) => { + eprintln!( + "warning: failed to interpret task details: {error:?}: {details:?}" + ); + return; + } + Ok(status) => status, + }; + + if status.disabled { + println!(" multicast feature is disabled"); + return; + } + + const GROUPS_CREATED: &str = "groups created (Creating->Active):"; + const GROUPS_DELETED: &str = "groups deleted (cleanup):"; + const GROUPS_VERIFIED: &str = "groups verified (Active):"; + const EMPTY_GROUPS_MARKED: &str = "empty groups marked for deletion:"; + const MEMBERS_PROCESSED: &str = "members processed:"; + const MEMBERS_DELETED: &str = "members deleted:"; + const WIDTH: usize = const_max_len(&[ + GROUPS_CREATED, + GROUPS_DELETED, + GROUPS_VERIFIED, + EMPTY_GROUPS_MARKED, + MEMBERS_PROCESSED, + MEMBERS_DELETED, + ]) + 1; + const NUM_WIDTH: usize = 3; + + if !status.errors.is_empty() { + println!( + " task did not complete successfully! ({} errors)", + status.errors.len() + ); + for error in &status.errors { + println!(" > {error}"); + } + } + + println!( + " {GROUPS_CREATED:NUM_WIDTH$}", + status.groups_created + ); + println!( + " {GROUPS_DELETED:NUM_WIDTH$}", + status.groups_deleted + ); + println!( + " {GROUPS_VERIFIED:NUM_WIDTH$}", + status.groups_verified + ); + println!( + " {EMPTY_GROUPS_MARKED:NUM_WIDTH$}", + status.empty_groups_marked + ); + println!( + " {MEMBERS_PROCESSED:NUM_WIDTH$}", + status.members_processed + ); + println!( + " {MEMBERS_DELETED:NUM_WIDTH$}", + status.members_deleted + ); +} + fn print_task_phantom_disks(details: &serde_json::Value) { #[derive(Deserialize)] struct TaskSuccess { diff --git a/dev-tools/omdb/tests/successes.out b/dev-tools/omdb/tests/successes.out index d6d6f89b40b..b7c5d28cca4 100644 --- a/dev-tools/omdb/tests/successes.out +++ b/dev-tools/omdb/tests/successes.out @@ -85,6 +85,36 @@ stderr: note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=disable note: database schema version matches expected () ============================================= +EXECUTING COMMAND: omdb ["db", "multicast", "groups"] +termination: Exited(0) +--------------------------------------------- +stdout: +ID NAME STATE MULTICAST_IP RANGE UNDERLAY_IP SOURCES MEMBERS VNI CREATED +--------------------------------------------- +stderr: +note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=disable +note: database schema version matches expected () +============================================= +EXECUTING COMMAND: omdb ["db", "multicast", "members"] +termination: Exited(0) +--------------------------------------------- +stdout: +ID GROUP_NAME PARENT_ID STATE MULTICAST_IP SOURCES SLED_ID CREATED +--------------------------------------------- +stderr: +note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=disable +note: database schema version matches expected () +============================================= +EXECUTING COMMAND: omdb ["db", "multicast", "pools"] +termination: Exited(0) +--------------------------------------------- +stdout: +no multicast IP pools found +--------------------------------------------- +stderr: +note: using database URL postgresql://root@[::1]:REDACTED_PORT/omicron?sslmode=disable +note: database schema version matches expected () +============================================= EXECUTING COMMAND: omdb ["db", "sleds"] termination: Exited(0) --------------------------------------------- @@ -845,7 +875,12 @@ task: "multicast_reconciler" configured period: every m last completed activation: , triggered by started at (s ago) and ran for ms -warning: unknown background task: "multicast_reconciler" (don't know how to interpret details: Object {"disabled": Bool(false), "empty_groups_marked": Number(0), "errors": Array [], "groups_created": Number(0), "groups_deleted": Number(0), "groups_verified": Number(0), "members_deleted": Number(0), "members_processed": Number(0)}) + groups created (Creating->Active): 0 + groups deleted (cleanup): 0 + groups verified (Active): 0 + empty groups marked for deletion: 0 + members processed: 0 + members deleted: 0 task: "phantom_disks" configured period: every s @@ -1536,7 +1571,12 @@ task: "multicast_reconciler" configured period: every m last completed activation: , triggered by started at (s ago) and ran for ms -warning: unknown background task: "multicast_reconciler" (don't know how to interpret details: Object {"disabled": Bool(false), "empty_groups_marked": Number(0), "errors": Array [], "groups_created": Number(0), "groups_deleted": Number(0), "groups_verified": Number(0), "members_deleted": Number(0), "members_processed": Number(0)}) + groups created (Creating->Active): 0 + groups deleted (cleanup): 0 + groups verified (Active): 0 + empty groups marked for deletion: 0 + members processed: 0 + members deleted: 0 task: "phantom_disks" configured period: every s diff --git a/dev-tools/omdb/tests/test_all_output.rs b/dev-tools/omdb/tests/test_all_output.rs index 92c6e4e186d..dab5120f8af 100644 --- a/dev-tools/omdb/tests/test_all_output.rs +++ b/dev-tools/omdb/tests/test_all_output.rs @@ -96,6 +96,7 @@ async fn test_omdb_usage_errors() { &["db", "sitrep", "--help"], &["db", "saga"], &["db", "snapshots"], + &["db", "multicast"], &["db", "network"], &["mgs"], &["nexus"], @@ -210,6 +211,9 @@ async fn test_omdb_success_cases() { &["db", "dns", "diff", "external", "2"], &["db", "dns", "names", "external", "2"], &["db", "instances"], + &["db", "multicast", "groups"], + &["db", "multicast", "members"], + &["db", "multicast", "pools"], &["db", "sleds"], &["db", "sleds", "-F", "discretionary"], &["mgs", "inventory"], diff --git a/dev-tools/omdb/tests/test_multicast.rs b/dev-tools/omdb/tests/test_multicast.rs new file mode 100644 index 00000000000..d1d761fd372 --- /dev/null +++ b/dev-tools/omdb/tests/test_multicast.rs @@ -0,0 +1,728 @@ +// 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/. + +#![cfg(feature = "multicast")] +//! Tests for omdb multicast commands with real data. +//! +//! These tests verify that omdb correctly formats multicast data by creating +//! actual multicast pools, groups, and members, then running omdb commands +//! and checking the output. + +use std::net::IpAddr; +use std::process::Command; +use std::time::Duration; + +use dropshot::test_util::ClientTestContext; +use futures::future::join3; +use nexus_db_queries::db::fixed_data::silo::DEFAULT_SILO; +use nexus_test_utils::http_testing::{AuthnMode, NexusRequest}; +use nexus_test_utils::resource_helpers::{ + create_default_ip_pools, create_instance_with, create_multicast_ip_pool, + create_project, link_ip_pool, object_put_upsert, objects_list_page_authz, +}; +use nexus_test_utils_macros::nexus_test; +use nexus_types::external_api::instance::InstanceNetworkInterfaceAttachment; +use nexus_types::external_api::multicast::{ + InstanceMulticastGroupJoin, MulticastGroup, MulticastGroupMember, +}; +use nexus_types::identity::Resource; +use omicron_common::address::{IpRange, Ipv4Range}; +use omicron_common::api::external::Instance; +use omicron_test_utils::dev::poll::{self, CondCheckError, wait_for_condition}; + +type ControlPlaneTestContext = + nexus_test_utils::ControlPlaneTestContext; + +const PROJECT_NAME: &str = "omdb-test-project"; + +/// Path to the omdb binary - set by cargo when running tests +const CMD_OMDB: &str = env!("CARGO_BIN_EXE_omdb"); + +// Timeout constants for test operations +const POLL_INTERVAL: Duration = Duration::from_millis(80); +const MULTICAST_OPERATION_TIMEOUT: Duration = Duration::from_secs(120); + +/// Run an omdb command and return its stdout. +fn run_omdb(db_url: &str, args: &[&str]) -> String { + let output = Command::new(CMD_OMDB) + .env("OMDB_DB_URL", db_url) + .args(args) + .output() + .expect("failed to execute omdb"); + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + panic!("omdb command failed with args {args:?}:\nstderr: {stderr}"); + } + + String::from_utf8_lossy(&output.stdout).to_string() +} + +/// Build URL for a specific multicast group by name. +fn mcast_group_url(group_name: &str) -> String { + format!("/v1/multicast-groups/{group_name}") +} + +/// Create a multicast IP pool and link it to the default silo. +async fn create_multicast_pool_linked( + client: &ClientTestContext, + pool_name: &str, + ip_range: Option, +) { + create_multicast_ip_pool(client, pool_name, ip_range).await; + link_ip_pool(client, pool_name, &DEFAULT_SILO.id(), false).await; +} + +/// Convenience function to wait for a group to become "Active". +async fn wait_for_group_active( + client: &ClientTestContext, + group_name: &str, +) -> MulticastGroup { + match wait_for_condition( + || async { + let group: MulticastGroup = + NexusRequest::object_get(client, &mcast_group_url(group_name)) + .authn_as(AuthnMode::PrivilegedUser) + .execute_and_parse_unwrap() + .await; + if group.state == "Active" { + Ok(group) + } else { + Err(CondCheckError::<()>::NotYet) + } + }, + &POLL_INTERVAL, + &MULTICAST_OPERATION_TIMEOUT, + ) + .await + { + Ok(group) => group, + Err(poll::Error::TimedOut(elapsed)) => { + panic!( + "group {group_name} did not reach state 'Active' within {elapsed:?}" + ); + } + Err(poll::Error::PermanentError(err)) => { + panic!( + "failed waiting for group {group_name} to reach 'Active': {err:?}" + ); + } + } +} + +/// Wait for a specific member to reach the expected state. +async fn wait_for_member_state( + cptestctx: &ControlPlaneTestContext, + group_name: &str, + instance_id: uuid::Uuid, + expected_state: nexus_db_model::MulticastGroupMemberState, +) -> MulticastGroupMember { + let client = &cptestctx.external_client; + let expected_state_str = expected_state.to_string(); + + match wait_for_condition( + || async { + let url = format!("/v1/multicast-groups/{group_name}/members"); + let members = + objects_list_page_authz::(client, &url) + .await + .items; + if let Some(member) = + members.iter().find(|m| m.instance_id == instance_id) + { + if member.state == expected_state_str { + Ok(member.clone()) + } else { + Err(CondCheckError::<()>::NotYet) + } + } else { + Err(CondCheckError::<()>::NotYet) + } + }, + &POLL_INTERVAL, + &MULTICAST_OPERATION_TIMEOUT, + ) + .await + { + Ok(member) => member, + Err(poll::Error::TimedOut(elapsed)) => { + panic!( + "member {instance_id} in group {group_name} did not reach state '{expected_state_str}' within {elapsed:?}" + ); + } + Err(poll::Error::PermanentError(err)) => { + panic!( + "failed waiting for member {instance_id} in group {group_name}: {err:?}" + ); + } + } +} + +/// Create an instance for multicast testing. +async fn create_test_instance( + client: &ClientTestContext, + project_name: &str, + instance_name: &str, + start: bool, +) -> Instance { + create_instance_with( + client, + project_name, + instance_name, + &InstanceNetworkInterfaceAttachment::DefaultIpv4, + vec![], + vec![], + start, + None, + None, + vec![], + ) + .await +} + +/// Test omdb multicast pools command. +#[nexus_test] +async fn test_omdb_multicast_pools(cptestctx: &ControlPlaneTestContext) { + let db_url = cptestctx.database.listen_url().to_string(); + let client = &cptestctx.external_client; + + // Before creating any pools, should show "no multicast IP pools found" + let output = run_omdb(&db_url, &["db", "multicast", "pools"]); + assert!( + output.contains("no multicast IP pools found"), + "Expected empty pool message, got: {output}" + ); + + // Create a multicast pool (no silo linking needed for pools-only test) + create_multicast_ip_pool(client, "test-mcast-pool", None).await; + + // Now should show the pool with all columns + let output = run_omdb(&db_url, &["db", "multicast", "pools"]); + // pool name + assert!( + output.contains("test-mcast-pool"), + "Expected pool name in output, got: {output}" + ); + // first address (default range from test-utils: 224.1.0.0 - 224.1.255.255) + assert!( + output.contains("224.1.0.0"), + "Expected first address in output, got: {output}" + ); + // last address + assert!( + output.contains("224.1.255.255"), + "Expected last address in output, got: {output}" + ); +} + +/// Test omdb multicast groups, members, and info commands. +/// +/// This consolidated test verifies all multicast commands work with actual data. +#[nexus_test] +async fn test_omdb_multicast_commands(cptestctx: &ControlPlaneTestContext) { + let db_url = cptestctx.database.listen_url().to_string(); + let client = &cptestctx.external_client; + + // Setup: create pools and project + join3( + create_default_ip_pools(client), + create_project(client, PROJECT_NAME), + create_multicast_pool_linked(client, "test-mcast-pool", None), + ) + .await; + + // Create an instance without multicast groups first + let instance = create_test_instance( + client, + PROJECT_NAME, + "test-instance", + false, // don't start + ) + .await; + + // Add a multicast member via API (this implicitly creates the group) + // Use instance-centric join endpoint: PUT /v1/instances/{instance}/multicast-groups/{group} + let join_url = format!( + "/v1/instances/{}/multicast-groups/test-mcast-group?project={PROJECT_NAME}", + instance.identity.id + ); + + object_put_upsert::<_, MulticastGroupMember>( + client, + &join_url, + &InstanceMulticastGroupJoin { + source_ips: None, // ASM (Any-Source Multicast) + ip_version: None, + }, + ) + .await; + + // Wait for the group to become "Active" + wait_for_group_active(client, "test-mcast-group").await; + + // Get the group details for later tests + let group_url = mcast_group_url("test-mcast-group"); + let group: MulticastGroup = NexusRequest::object_get(client, &group_url) + .authn_as(AuthnMode::PrivilegedUser) + .execute() + .await + .expect("failed to get group") + .parsed_body() + .expect("failed to parse group"); + + // Test: omdb db multicast groups + let output = run_omdb(&db_url, &["db", "multicast", "groups"]); + // group id + assert!( + output.contains(&group.identity.id.to_string()), + "Expected group id in output, got: {output}" + ); + // group name + assert!( + output.contains("test-mcast-group"), + "Expected group name in output, got: {output}" + ); + // state + assert!( + output.contains("Active"), + "Expected state 'Active' in output, got: {output}" + ); + // multicast ip + assert!( + output.contains(&group.multicast_ip.to_string()), + "Expected multicast ip in output, got: {output}" + ); + // range (ASM for 224.x.x.x) + assert!( + output.contains("ASM"), + "Expected range 'ASM' in output, got: {output}" + ); + // vni (column exists but VNI is internal, not exposed in the external API) + assert!( + output.contains("VNI"), + "Expected VNI column in output, got: {output}" + ); + // members (instance@sled format, "-" when not started) + assert!( + output.contains("test-instance@-"), + "Expected member 'test-instance@-' in output, got: {output}" + ); + + // Test: omdb db multicast groups --state active + let output = + run_omdb(&db_url, &["db", "multicast", "groups", "--state", "active"]); + assert!( + output.contains("test-mcast-group"), + "Expected group name with state filter, got: {output}" + ); + + // Test: omdb db multicast groups --pool + let output = run_omdb( + &db_url, + &["db", "multicast", "groups", "--pool", "test-mcast-pool"], + ); + assert!( + output.contains("test-mcast-group"), + "Expected group name with pool filter, got: {output}" + ); + + // Test: omdb db multicast members + let output = run_omdb(&db_url, &["db", "multicast", "members"]); + // group name + assert!( + output.contains("test-mcast-group"), + "Expected group name in members output, got: {output}" + ); + // parent id (instance id) + assert!( + output.contains(&instance.identity.id.to_string()), + "Expected parent id in members output, got: {output}" + ); + // multicast ip + let group_ip = group.multicast_ip.to_string(); + assert!( + output.contains(&group_ip), + "Expected multicast ip in members output, got: {output}" + ); + // sources ("-" = any-source member) + let has_any_source = output.lines().any(|line| { + line.contains(&instance.identity.id.to_string()) && line.contains(" - ") + }); + assert!( + has_any_source, + "Expected '-' for any-source member, got: {output}" + ); + + // Test: omdb db multicast members --group-name + let output = run_omdb( + &db_url, + &["db", "multicast", "members", "--group-name", "test-mcast-group"], + ); + assert!( + output.contains(&instance.identity.id.to_string()), + "Expected instance ID with group-name filter, got: {output}" + ); + + // Test: omdb db multicast members --group-ip (reuses group_ip from above) + let output = run_omdb( + &db_url, + &["db", "multicast", "members", "--group-ip", &group_ip], + ); + assert!( + output.contains(&instance.identity.id.to_string()), + "Expected instance ID with group-ip filter, got: {output}" + ); + + // Test: omdb db multicast members --group-id + let group_id = group.identity.id.to_string(); + let output = run_omdb( + &db_url, + &["db", "multicast", "members", "--group-id", &group_id], + ); + assert!( + output.contains(&instance.identity.id.to_string()), + "Expected instance ID with group-id filter, got: {output}" + ); + + // Test: omdb db multicast members --state left + // Wait for the RPW reconciler to transition member to "Left" state + // (instance isn't running, so no sled_id assignment) + wait_for_member_state( + cptestctx, + "test-mcast-group", + instance.identity.id, + nexus_db_model::MulticastGroupMemberState::Left, + ) + .await; + let output = + run_omdb(&db_url, &["db", "multicast", "members", "--state", "left"]); + assert!( + output.contains(&instance.identity.id.to_string()), + "Expected instance ID with state=left filter, got: {output}" + ); + + // Test: omdb db multicast members --sled-id + // Create a started instance so the member gets a sled_id + let started_instance = create_test_instance( + client, + PROJECT_NAME, + "started-instance", + true, // start the instance + ) + .await; + + // Add member to a new group for the started instance + let sled_join_url = format!( + "/v1/instances/{}/multicast-groups/sled-test-group?project={PROJECT_NAME}", + started_instance.identity.id + ); + object_put_upsert::<_, MulticastGroupMember>( + client, + &sled_join_url, + &InstanceMulticastGroupJoin { source_ips: None, ip_version: None }, + ) + .await; + + wait_for_group_active(client, "sled-test-group").await; + + // Query members by sled_id - the started instance should be on first_sled + let sled_id = cptestctx.first_sled_id().to_string(); + let output = run_omdb( + &db_url, + &["db", "multicast", "members", "--sled-id", &sled_id], + ); + assert!( + output.contains(&started_instance.identity.id.to_string()), + "Expected started instance ID with sled-id filter, got: {output}" + ); + + // Test: omdb db multicast members --state joined + // Wait for the started instance's member to reach "Joined" state + wait_for_member_state( + cptestctx, + "sled-test-group", + started_instance.identity.id, + nexus_db_model::MulticastGroupMemberState::Joined, + ) + .await; + + // Now test the --state joined filter + let output_joined = + run_omdb(&db_url, &["db", "multicast", "members", "--state", "joined"]); + assert!( + output_joined.contains(&started_instance.identity.id.to_string()), + "Expected started instance in joined state, got: {output_joined}" + ); + // state column shows "Joined" + assert!( + output_joined.contains("Joined"), + "Expected 'Joined' state in members output, got: {output_joined}" + ); + // sled_id column shows the sled UUID + assert!( + output_joined.contains(&sled_id), + "Expected sled_id in members output, got: {output_joined}" + ); + + // Verify info for started instance shows sled serial (not "-") + let output_info = run_omdb( + &db_url, + &["db", "multicast", "info", "--name", "sled-test-group"], + ); + // member instance name + assert!( + output_info.contains("started-instance"), + "Expected 'started-instance' in info members, got: {output_info}" + ); + // underlay group should be present for active group + assert!( + output_info.contains("UNDERLAY GROUP"), + "Expected 'UNDERLAY GROUP' section in info output, got: {output_info}" + ); + + // Verify groups output shows started instance with sled serial (not "-") + let output_groups = run_omdb(&db_url, &["db", "multicast", "groups"]); + // sled-test-group should show "started-instance@" not "started-instance@-" + assert!( + output_groups.contains("started-instance@"), + "Expected 'started-instance@' in groups members column, got: {output_groups}" + ); + // The sled serial should appear (not just "-") + // Note: test sled serial is typically "serial0" or similar + assert!( + !output_groups.contains("started-instance@-"), + "Started instance should have sled serial, not '-', got: {output_groups}" + ); + + // Verify underlay_ip column shows an IP for active groups + // Active groups with joined members should have an underlay group assigned + // The underlay IP is in the ff04::/16 range (admin-local IPv6 multicast) + assert!( + output_groups.contains("ff04:"), + "Expected underlay_ip (ff04:*) for active group in groups output, got: {output_groups}" + ); + + // Verify started instance is not in "Left" state + let output_left = + run_omdb(&db_url, &["db", "multicast", "members", "--state", "left"]); + assert!( + !output_left.contains(&started_instance.identity.id.to_string()), + "Started instance should not be in 'Left' state, got: {output_left}" + ); + + // Test: combined filters (--group-name + --state) + // The started instance's member should appear when filtering by both + let output_combined = run_omdb( + &db_url, + &[ + "db", + "multicast", + "members", + "--group-name", + "sled-test-group", + "--state", + "joined", + ], + ); + assert!( + output_combined.contains(&started_instance.identity.id.to_string()), + "Expected started instance with combined filters, got: {output_combined}" + ); + + // Test: combined filters that should return empty (wrong group + state) + let output_combined_empty = run_omdb( + &db_url, + &[ + "db", + "multicast", + "members", + "--group-name", + "test-mcast-group", + "--state", + "joined", + ], + ); + // test-mcast-group has a non-started instance, so it should not be in "Joined" state + assert!( + !output_combined_empty + .contains(&started_instance.identity.id.to_string()), + "Started instance should not appear in wrong group filter, got: {output_combined_empty}" + ); + + // Test: omdb db multicast info --name + let output = run_omdb( + &db_url, + &["db", "multicast", "info", "--name", "test-mcast-group"], + ); + // section header + assert!( + output.contains("MULTICAST GROUP"), + "Expected 'MULTICAST GROUP' header in info output, got: {output}" + ); + // id + assert!( + output.contains(&group.identity.id.to_string()), + "Expected group id in info output, got: {output}" + ); + // name + assert!( + output.contains("test-mcast-group"), + "Expected group name in info output, got: {output}" + ); + // state + assert!( + output.contains("Active"), + "Expected state 'Active' in info output, got: {output}" + ); + // multicast ip + assert!( + output.contains(&group.multicast_ip.to_string()), + "Expected multicast ip in info output, got: {output}" + ); + // vni (field exists but VNI is internal, not exposed in the external API) + assert!( + output.contains("vni:"), + "Expected vni field in info output, got: {output}" + ); + // ip pool + assert!( + output.contains("test-mcast-pool"), + "Expected pool name in info output, got: {output}" + ); + // members section + assert!( + output.contains("MEMBERS"), + "Expected 'MEMBERS' section in info output, got: {output}" + ); + // member instance name + assert!( + output.contains("test-instance"), + "Expected instance name in info members, got: {output}" + ); + // member sled ("-" when not started) - check specific line pattern + // More specific than just contains("-") which could match table separators + let has_sled_dash = output + .lines() + .any(|line| line.contains("test-instance") && line.contains(" - ")); + assert!( + has_sled_dash, + "Expected sled '-' for non-started instance on same line as instance name, got: {output}" + ); + + // Test: omdb db multicast info --ip + let output = + run_omdb(&db_url, &["db", "multicast", "info", "--ip", &group_ip]); + assert!( + output.contains("test-mcast-group"), + "Expected group name when querying by IP, got: {output}" + ); + + // Test: omdb db multicast info --group-id (reuses group_id from members test) + let output = run_omdb( + &db_url, + &["db", "multicast", "info", "--group-id", &group_id], + ); + assert!( + output.contains("test-mcast-group"), + "Expected group name when querying by ID, got: {output}" + ); + + // Test SSM (Source-Specific Multicast) - group in 232/8 range + // SSM range is 232.0.0.0/8 for IPv4, ff3x::/32 for IPv6 + let ssm_range = IpRange::V4( + Ipv4Range::new( + std::net::Ipv4Addr::new(232, 1, 0, 0), + std::net::Ipv4Addr::new(232, 1, 0, 255), + ) + .unwrap(), + ); + create_multicast_pool_linked(client, "test-ssm-pool", Some(ssm_range)) + .await; + + let ssm_instance = + create_test_instance(client, PROJECT_NAME, "ssm-instance", false).await; + + let ssm_join_url = format!( + "/v1/instances/{}/multicast-groups/ssm-group?project={PROJECT_NAME}", + ssm_instance.identity.id + ); + object_put_upsert::<_, MulticastGroupMember>( + client, + &ssm_join_url, + &InstanceMulticastGroupJoin { + source_ips: Some(vec![ + "10.0.0.1".parse::().unwrap(), + "10.0.0.2".parse::().unwrap(), + ]), + ip_version: None, + }, + ) + .await; + + wait_for_group_active(client, "ssm-group").await; + + // Verify SSM group shows in groups list with sources + let output = run_omdb(&db_url, &["db", "multicast", "groups"]); + assert!( + output.contains("ssm-group"), + "Expected SSM group in output, got: {output}" + ); + // Verify SSM is shown in RANGE column (232.x.x.x = SSM range) + assert!( + output.contains("SSM"), + "Expected SSM in range column, got: {output}" + ); + // Verify ASM is shown for 224.x.x.x range (test-mcast-group) + assert!( + output.contains("ASM"), + "Expected ASM in range column, got: {output}" + ); + // Verify SSM source IPs + assert!( + output.contains("10.0.0.1") && output.contains("10.0.0.2"), + "Expected SSM source IPs in output, got: {output}" + ); + + // Verify SSM sources show in info command + let output = + run_omdb(&db_url, &["db", "multicast", "info", "--name", "ssm-group"]); + assert!( + output.contains("10.0.0.1") || output.contains("10.0.0.2"), + "Expected SSM source IPs in info output, got: {output}" + ); + + // Test: omdb db multicast members shows sources per member + let output = run_omdb(&db_url, &["db", "multicast", "members"]); + // SSM member should show its sources + assert!( + output.contains("10.0.0.1") || output.contains("10.0.0.2"), + "Expected SSM member sources in members output, got: {output}" + ); + + // Test: omdb db multicast members --source-ip + // Filter by SSM source IP - should find SSM member + let output = run_omdb( + &db_url, + &["db", "multicast", "members", "--source-ip", "10.0.0.1"], + ); + assert!( + output.contains(&ssm_instance.identity.id.to_string()), + "Expected SSM instance with source-ip filter, got: {output}" + ); + // Members without sources should not appear for any source-ip filter + assert!( + !output.contains(&instance.identity.id.to_string()), + "Member without sources should not appear, got: {output}" + ); + + // Test: --source-ip with non-existent IP returns no members + let output = run_omdb( + &db_url, + &["db", "multicast", "members", "--source-ip", "10.99.99.99"], + ); + assert!( + !output.contains(&ssm_instance.identity.id.to_string()), + "No members should match non-existent source IP, got: {output}" + ); +} diff --git a/dev-tools/omdb/tests/usage_errors.out b/dev-tools/omdb/tests/usage_errors.out index 3795a6be563..2c8f2d19b79 100644 --- a/dev-tools/omdb/tests/usage_errors.out +++ b/dev-tools/omdb/tests/usage_errors.out @@ -146,6 +146,7 @@ Commands: instances Alias to `omdb instance list` network Print information about the network migrations Print information about migrations + multicast Print information about multicast groups snapshots Print information about snapshots validate Validate the contents of the database volumes Print information about volumes @@ -214,6 +215,7 @@ Commands: instances Alias to `omdb instance list` network Print information about the network migrations Print information about migrations + multicast Print information about multicast groups snapshots Print information about snapshots validate Validate the contents of the database volumes Print information about volumes @@ -874,6 +876,41 @@ Database Options: --include-deleted whether to include soft-deleted records when enumerating objects that can be soft-deleted +Safety Options: + -w, --destructive Allow potentially-destructive subcommands +============================================= +EXECUTING COMMAND: omdb ["db", "multicast"] +termination: Exited(2) +--------------------------------------------- +stdout: +--------------------------------------------- +stderr: +Print information about multicast groups + +Usage: omdb db multicast [OPTIONS] + +Commands: + groups List all multicast groups + members List all multicast group members + pools List multicast IP pools and their ranges + info Get detailed info for a multicast group + help Print this message or the help of the given subcommand(s) + +Options: + --log-level log level filter [env: LOG_LEVEL=] [default: warn] + --color Color output [default: auto] [possible values: auto, always, never] + -h, --help Print help + +Connection Options: + --db-url URL of the database SQL interface [env: OMDB_DB_URL=] + --dns-server [env: OMDB_DNS_SERVER=] + +Database Options: + --fetch-limit limit to apply to queries that fetch rows [env: + OMDB_FETCH_LIMIT=] [default: 500] + --include-deleted whether to include soft-deleted records when enumerating objects + that can be soft-deleted + Safety Options: -w, --destructive Allow potentially-destructive subcommands ============================================= diff --git a/dev-tools/xtask/src/virtual_hardware.rs b/dev-tools/xtask/src/virtual_hardware.rs index 0f58bf5bc4b..8bd6593edad 100644 --- a/dev-tools/xtask/src/virtual_hardware.rs +++ b/dev-tools/xtask/src/virtual_hardware.rs @@ -118,7 +118,7 @@ const ZONEADM: &'static str = "/usr/sbin/zoneadm"; const SIDECAR_LITE_COMMIT: &'static str = "69fae2ac230cd29f4ea2bf3bcbd6d99554c7bc47"; -const SOFTNPU_COMMIT: &'static str = "e4c3130538b192988737161f4a4dc97272f02b9c"; +const SOFTNPU_COMMIT: &'static str = "284c6830722548714128e63ea04bcca78ee27154"; const PXA_MAC_DEFAULT: &'static str = "a8:e1:de:01:70:1d"; const PXA_WARNING: &'static str = r#" You have not set up the proxy-ARP environment variables diff --git a/env.sh b/env.sh index 6a84c35902a..114b53f07ed 100644 --- a/env.sh +++ b/env.sh @@ -12,6 +12,7 @@ export PATH="$OMICRON_WS/out/cockroachdb/bin:$PATH" export PATH="$OMICRON_WS/out/clickhouse:$PATH" export PATH="$OMICRON_WS/out/dendrite-stub/bin:$PATH" export PATH="$OMICRON_WS/out/mgd/root/opt/oxide/mgd/bin:$PATH" +export PATH="$OMICRON_WS/out/mg-ddm/root/opt/oxide/mg-ddm/bin:$PATH" # if xtrace was set previously, do not unset it case $OLD_SHELL_OPTS in diff --git a/internal-dns/types/src/config.rs b/internal-dns/types/src/config.rs index d5bef144343..5b4f736e2c5 100644 --- a/internal-dns/types/src/config.rs +++ b/internal-dns/types/src/config.rs @@ -163,6 +163,20 @@ pub struct DnsConfigBuilder { service_instances_sleds: BTreeMap>, } +/// Ports for the per-switch services published in internal DNS by +/// [`DnsConfigBuilder::host_zone_switch`]. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct HostSwitchZonePorts { + /// Dendrite (`dpd`) admin API port. + pub dendrite: u16, + /// Management Gateway Service (`mgs`) port. + pub mgs: u16, + /// Maghemite `mgd` admin API port. + pub mgd: u16, + /// Maghemite `ddmd` admin API port. + pub ddm: u16, +} + /// Describes a host of type "sled" in the control plane DNS zone #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Sled(SledUuid); @@ -396,10 +410,14 @@ impl DnsConfigBuilder { &mut self, sled_id: SledUuid, switch_zone_ip: Ipv6Addr, - dendrite_port: u16, - mgs_port: u16, - mgd_port: u16, + ports: HostSwitchZonePorts, ) -> anyhow::Result<()> { + let HostSwitchZonePorts { + dendrite: dendrite_port, + mgs: mgs_port, + mgd: mgd_port, + ddm: ddm_port, + } = ports; let zone = self.host_dendrite(sled_id, switch_zone_ip)?; self.service_backend_zone(ServiceName::Dendrite, &zone, dendrite_port)?; self.service_backend_zone( @@ -407,7 +425,8 @@ impl DnsConfigBuilder { &zone, mgs_port, )?; - self.service_backend_zone(ServiceName::Mgd, &zone, mgd_port) + self.service_backend_zone(ServiceName::Mgd, &zone, mgd_port)?; + self.service_backend_zone(ServiceName::Ddm, &zone, ddm_port) } /// Higher-level shorthand for adding a Nexus zone with both its internal @@ -731,7 +750,9 @@ impl DnsConfigBuilder { #[cfg(test)] mod test { - use super::{DnsConfigBuilder, Host, ServiceName}; + use super::{ + DnsConfigBuilder, DnsRecord, Host, HostSwitchZonePorts, ServiceName, + }; use crate::{config::Zone, names::DNS_ZONE}; use omicron_common::api::external::Generation; use omicron_uuid_kinds::{OmicronZoneUuid, SledUuid}; @@ -779,6 +800,8 @@ mod test { "_oximeter-reader._tcp", ); assert_eq!(ServiceName::Dendrite.dns_name(), "_dendrite._tcp",); + assert_eq!(ServiceName::Mgd.dns_name(), "_mgd._tcp",); + assert_eq!(ServiceName::Ddm.dns_name(), "_ddm._tcp",); assert_eq!( ServiceName::CruciblePantry.dns_name(), "_crucible-pantry._tcp", @@ -796,6 +819,71 @@ mod test { ); } + #[test] + fn host_zone_switch_publishes_all_services() { + let sled_uuid: SledUuid = + "001de000-51ed-4000-8000-000000000001".parse().unwrap(); + let switch_zone_ip = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1); + + // Use distinct port numbers so an arg-order swap in `host_zone_switch` + // surfaces as a port mismatch on the affected service. + let dendrite_port = 11; + let mgs_port = 13; + let mgd_port = 17; + let ddm_port = 19; + + let mut builder = DnsConfigBuilder::new(); + builder + .host_zone_switch( + sled_uuid, + switch_zone_ip, + HostSwitchZonePorts { + dendrite: dendrite_port, + mgs: mgs_port, + mgd: mgd_port, + ddm: ddm_port, + }, + ) + .unwrap(); + + let config = builder.build_full_config_for_initial_generation(); + + let mut by_name: BTreeMap<&str, &[DnsRecord]> = BTreeMap::new(); + for zone in &config.zones { + for (name, records) in &zone.records { + by_name.insert(name.as_str(), records.as_slice()); + } + } + + for (expected_name, expected_port) in [ + ("_dendrite._tcp", dendrite_port), + ("_mgs._tcp", mgs_port), + ("_mgd._tcp", mgd_port), + ("_ddm._tcp", ddm_port), + ] { + let records = by_name.get(expected_name).unwrap_or_else(|| { + panic!( + "expected {expected_name} in published switch-zone \ + services; got {by_name:?}" + ) + }); + let srv_port = records + .iter() + .find_map(|r| match r { + DnsRecord::Srv(s) => Some(s.port), + _ => None, + }) + .unwrap_or_else(|| { + panic!("no SRV record for {expected_name}: {records:?}") + }); + + assert_eq!( + srv_port, expected_port, + "wrong SRV port for {expected_name}" + ); + } + } + #[test] fn display_hosts() { let sled_uuid = SledUuid::nil(); diff --git a/internal-dns/types/src/names.rs b/internal-dns/types/src/names.rs index 73b2439e48e..105d0222f3c 100644 --- a/internal-dns/types/src/names.rs +++ b/internal-dns/types/src/names.rs @@ -75,6 +75,7 @@ pub enum ServiceName { BoundaryNtp, InternalNtp, Mgd, + Ddm, } impl ServiceName { @@ -116,6 +117,7 @@ impl ServiceName { ServiceName::BoundaryNtp => "boundary-ntp", ServiceName::InternalNtp => "internal-ntp", ServiceName::Mgd => "mgd", + ServiceName::Ddm => "ddm", } } @@ -144,7 +146,8 @@ impl ServiceName { | ServiceName::CruciblePantry | ServiceName::BoundaryNtp | ServiceName::InternalNtp - | ServiceName::Mgd => { + | ServiceName::Mgd + | ServiceName::Ddm => { format!("_{}._tcp", self.service_kind()) } ServiceName::SledAgent(id) => { diff --git a/nexus/Cargo.toml b/nexus/Cargo.toml index dab6d2787a4..04e4a5e2af0 100644 --- a/nexus/Cargo.toml +++ b/nexus/Cargo.toml @@ -8,6 +8,7 @@ license = "MPL-2.0" workspace = true [features] +default = ["multicast"] multicast = [] [build-dependencies] @@ -96,10 +97,9 @@ qorb.workspace = true rand.workspace = true range-requests.workspace = true ref-cast.workspace = true -rdb-types.workspace = true +mg-api-types.workspace = true regex.workspace = true reqwest = { workspace = true, features = ["http2", "json"] } -reqwest012 = { workspace = true } ring.workspace = true samael.workspace = true schemars = { workspace = true, features = ["chrono", "uuid1"] } diff --git a/nexus/db-schema/src/schema.rs b/nexus/db-schema/src/schema.rs index 05d6c96b1db..1099c520092 100644 --- a/nexus/db-schema/src/schema.rs +++ b/nexus/db-schema/src/schema.rs @@ -3031,7 +3031,11 @@ table! { } // Allow multicast tables to appear together for NOT EXISTS subqueries -allow_tables_to_appear_in_same_query!(multicast_group, multicast_group_member); +allow_tables_to_appear_in_same_query!( + multicast_group, + underlay_multicast_group, + multicast_group_member +); allow_tables_to_appear_in_same_query!(user_data_export, snapshot, image); diff --git a/nexus/reconfigurator/execution/src/dns.rs b/nexus/reconfigurator/execution/src/dns.rs index 685c7c85e6f..0a85c4dd114 100644 --- a/nexus/reconfigurator/execution/src/dns.rs +++ b/nexus/reconfigurator/execution/src/dns.rs @@ -988,9 +988,8 @@ mod test { // the previous pass (i.e., that corresponds to an Omicron zone). // // There are some ServiceNames missing here because they are not part of - // our representative config (e.g., ClickhouseKeeper) or they don't - // currently have DNS record at all (e.g., SledAgent, Maghemite, Mgd, - // Tfport). + // our representative config (e.g., ClickhouseKeeper) or because they + // do not currently have a DNS record at all (e.g., SledAgent). let mut srv_kinds_expected = BTreeSet::from([ ServiceName::Clickhouse, ServiceName::ClickhouseNative, @@ -1001,6 +1000,8 @@ mod test { ServiceName::NexusLockstep, ServiceName::Oximeter, ServiceName::Dendrite, + ServiceName::Mgd, + ServiceName::Ddm, ServiceName::CruciblePantry, ServiceName::BoundaryNtp, ServiceName::InternalNtp, diff --git a/nexus/reconfigurator/execution/src/test_utils.rs b/nexus/reconfigurator/execution/src/test_utils.rs index cd46adacd0b..fdb17289225 100644 --- a/nexus/reconfigurator/execution/src/test_utils.rs +++ b/nexus/reconfigurator/execution/src/test_utils.rs @@ -113,10 +113,12 @@ pub fn overridables_for_test( let dendrite_port = cptestctx.dendrite.read().unwrap().get(&switch_slot).unwrap().port; let mgd_port = cptestctx.mgd.get(&switch_slot).unwrap().port; + let ddm_port = cptestctx.ddm.get(&switch_slot).unwrap().port; overrides.override_switch_zone_ip(sled_id, ip); overrides.override_dendrite_port(sled_id, dendrite_port); overrides.override_mgs_port(sled_id, mgs_port); overrides.override_mgd_port(sled_id, mgd_port); + overrides.override_ddm_port(sled_id, ddm_port); } overrides } diff --git a/nexus/reconfigurator/planning/src/example.rs b/nexus/reconfigurator/planning/src/example.rs index a1f865e2934..7dbbf3640dc 100644 --- a/nexus/reconfigurator/planning/src/example.rs +++ b/nexus/reconfigurator/planning/src/example.rs @@ -1854,7 +1854,8 @@ mod tests { | ServiceName::RepoDepot | ServiceName::ManagementGatewayService | ServiceName::Dendrite - | ServiceName::Mgd => { + | ServiceName::Mgd + | ServiceName::Ddm => { out.insert(service, Ok(())); } // InternalNtp is too large to fit in a single DNS packet and diff --git a/nexus/src/app/background/tasks/sync_switch_configuration.rs b/nexus/src/app/background/tasks/sync_switch_configuration.rs index 91dcc66b070..f4abc9e549b 100644 --- a/nexus/src/app/background/tasks/sync_switch_configuration.rs +++ b/nexus/src/app/background/tasks/sync_switch_configuration.rs @@ -30,13 +30,22 @@ use dpd_client::{Client as DpdClient, types as DpdTypes}; use futures::FutureExt; use futures::future::BoxFuture; use mg_admin_client::types::{ - AddStaticRoute4Request, AddStaticRoute6Request, ApplyRequest, - BestpathFanoutRequest, BgpPeerConfig, CheckerSource, - DeleteStaticRoute4Request, DeleteStaticRoute6Request, + ApplyRequest, BgpPeerConfig, UnnumberedBgpPeerConfig, +}; +use mg_api_types::bgp::config::{ + CheckerSource, Ipv4UnicastConfig, Ipv6UnicastConfig, JitterRange, + ShaperSource, +}; +use mg_api_types::bgp::policy::{ ImportExportPolicy4 as MgImportExportPolicy4, - ImportExportPolicy6 as MgImportExportPolicy6, Ipv4UnicastConfig, - Ipv6UnicastConfig, JitterRange, ShaperSource, StaticRoute4, - StaticRoute4List, StaticRoute6, StaticRoute6List, UnnumberedBgpPeerConfig, + ImportExportPolicy6 as MgImportExportPolicy6, +}; +use mg_api_types::rdb::prefix::{Prefix, Prefix4, Prefix6}; +use mg_api_types::rib::BestpathFanoutRequest; +use mg_api_types::static_routes::{ + AddStaticRoute4Request, AddStaticRoute6Request, DeleteStaticRoute4Request, + DeleteStaticRoute6Request, StaticRoute4, StaticRoute4List, StaticRoute6, + StaticRoute6List, }; use nexus_db_queries::{ context::OpContext, @@ -49,7 +58,6 @@ use omicron_common::{ address::{Ipv6Subnet, get_sled_address}, api::external::{DataPageParams, Name}, }; -use rdb_types::{Prefix, Prefix4, Prefix6}; use serde_json::json; use sled_agent_client::types::HostPortConfig; use sled_agent_types::early_networking::BgpConfig as SledBgpConfig; @@ -998,7 +1006,7 @@ impl BackgroundTask for SwitchPortSettingsManager { "switch_slot" => ?switch_slot, "config" => ?config, ); - if let Err(e) = client.bgp_apply_v2(config).await { + if let Err(e) = client.bgp_apply(config).await { error!(log, "error while applying bgp configuration"; "error" => ?e); } diff --git a/nexus/src/app/bgp.rs b/nexus/src/app/bgp.rs index 53cc41996c5..0ca95b2f294 100644 --- a/nexus/src/app/bgp.rs +++ b/nexus/src/app/bgp.rs @@ -177,7 +177,7 @@ impl super::Nexus { for r in &router_info { let asn = r.asn; - let selector = mg_admin_client::types::ExportedSelector { + let selector = mg_api_types::bgp::session::ExportedSelector { afi: None, asn, peer: None, @@ -199,12 +199,12 @@ impl super::Nexus { for (peer_id, exports) in exported { for ex in exports.iter() { let prefix = match ex { - rdb_types::Prefix::V4(v4) => { + mg_api_types::rdb::prefix::Prefix::V4(v4) => { oxnet::IpNet::V4(oxnet::Ipv4Net::new_unchecked( v4.value, v4.length, )) } - rdb_types::Prefix::V6(v6) => { + mg_api_types::rdb::prefix::Prefix::V6(v6) => { oxnet::IpNet::V6(oxnet::Ipv6Net::new_unchecked( v6.value, v6.length, )) diff --git a/nexus/src/app/crucible.rs b/nexus/src/app/crucible.rs index 0037143e66d..9b7dce4d78e 100644 --- a/nexus/src/app/crucible.rs +++ b/nexus/src/app/crucible.rs @@ -74,11 +74,9 @@ impl super::Nexus { &self, dataset: &db::model::CrucibleDataset, ) -> CrucibleAgentClient { - // Use reqwest012_client because the rev-pinned crucible-agent-client - // is still on reqwest 0.12. CrucibleAgentClient::new_with_client( &format!("http://{}", dataset.address()), - self.reqwest012_client.clone(), + self.reqwest_client.clone(), ) } diff --git a/nexus/src/app/disk.rs b/nexus/src/app/disk.rs index 32ce2894b71..7fe564a6b5b 100644 --- a/nexus/src/app/disk.rs +++ b/nexus/src/app/disk.rs @@ -564,11 +564,9 @@ impl super::Nexus { // that user's program can act accordingly. In a way, the user's // program is an externally driven saga instead. - // Use reqwest012_client because the rev-pinned - // crucible-pantry-client is still on reqwest 0.12. let client = crucible_pantry_client::Client::new_with_client( &format!("http://{}", endpoint), - self.reqwest012_client.clone(), + self.reqwest_client.clone(), ); let request = crucible_pantry_client::types::BulkWriteRequest { offset: param.offset, diff --git a/nexus/src/app/mod.rs b/nexus/src/app/mod.rs index 6778ea72598..6ea76fc86f5 100644 --- a/nexus/src/app/mod.rs +++ b/nexus/src/app/mod.rs @@ -224,18 +224,8 @@ pub struct Nexus { /// /// (This does not need to be in an `Arc` because `reqwest::Client` uses /// `Arc` internally.) - /// - /// Currently unused because all `new_with_client` call sites use - /// `reqwest012_client` for cross-repo dependencies that are still on - /// reqwest 0.12. This field will be used again once rev pins are updated. - #[allow(dead_code)] reqwest_client: reqwest::Client, - /// `reqwest012::Client` for cross-repo dependencies where the rev-pinned - /// dependency is still on reqwest 0.12. Remove once all rev pins are - /// updated. - reqwest012_client: reqwest012::Client, - /// Client to the timeseries database. timeseries_client: oximeter_db::Client, @@ -437,14 +427,6 @@ impl Nexus { .build() .map_err(|e| InlineErrorChain::new(&e).to_string())?; - // reqwest 0.12 client for cross-repo dependencies still on reqwest - // 0.12. Remove once all rev pins are updated. - let reqwest012_client = reqwest012::ClientBuilder::new() - .connect_timeout(std::time::Duration::from_secs(15)) - .timeout(std::time::Duration::from_secs(15)) - .build() - .map_err(|e| InlineErrorChain::new(&e).to_string())?; - // Client to the ClickHouse database. let timeseries_client = match &config.pkg.timeseries_db.address { None => { @@ -549,7 +531,6 @@ impl Nexus { producer_server: std::sync::Mutex::new(None), populate_status, reqwest_client, - reqwest012_client, timeseries_client, webhook_delivery_client, tunables: config.pkg.tunables.clone(), diff --git a/nexus/src/app/sagas/region_replacement_drive.rs b/nexus/src/app/sagas/region_replacement_drive.rs index b55a225d756..22716a0adf9 100644 --- a/nexus/src/app/sagas/region_replacement_drive.rs +++ b/nexus/src/app/sagas/region_replacement_drive.rs @@ -238,6 +238,42 @@ impl NexusSaga for SagaRegionReplacementDrive { // region replacement drive saga: action implementations +/// Walk a [`VolumeInfo`] tree to determine reconciliation status. +/// +/// Replaces the previous `VolumeStatus.active` boolean. The shape change +/// originated in crucible PR oxidecomputer/crucible#1928 and reached omicron +/// via propolis PR oxidecomputer/propolis#1132. +/// +/// # Returns +/// +/// `true` when every [`Upstairs`] leaf reports [`Active`] state with no +/// live-repair or reconcile in progress; `false` otherwise. +/// +/// [`VolumeInfo`]: propolis_client::types::VolumeInfo +/// [`Upstairs`]: propolis_client::types::VolumeInfo::Upstairs +/// [`Active`]: propolis_client::types::UpstairsInfoStatus::Active +fn volume_info_reconciled(info: &propolis_client::types::VolumeInfo) -> bool { + use propolis_client::types::{UpstairsInfoStatus, VolumeInfo}; + match info { + VolumeInfo::Volume { sub_volumes, read_only_parent } => { + sub_volumes.iter().all(volume_info_reconciled) + && read_only_parent + .as_deref() + .map_or(true, volume_info_reconciled) + } + VolumeInfo::Upstairs { + state, + live_repair_in_progress, + reconcile_in_progress, + .. + } => { + matches!(state, UpstairsInfoStatus::Active) + && !live_repair_in_progress + && !reconcile_in_progress + } + } +} + async fn srrd_set_saga_id( sagactx: NexusActionContext, ) -> Result<(), ActionError> { @@ -1576,7 +1612,7 @@ async fn execute_propolis_drive_action( // // Check if the Volume activated. - let result = client + let info = client .disk_volume_status() .id(disk.id()) .send() @@ -1590,24 +1626,26 @@ async fn execute_propolis_drive_action( "unexpected failure during \ `disk_volume_status`: {e}", ))), - })?; + })? + .into_inner() + .volume_info; - // If the Volume is active, then reconciliation finished - // successfully. + // Reconciliation finished iff every upstairs leaf reports Active + // with no live-repair or reconcile in progress. // - // There's a few reasons it may not be active yet: + // Reasons it may not be reconciled yet: // - // - Propolis could be shutting down, and tearing down the Upstairs - // in the process (which deactivates the Volume) + // - Propolis could be shutting down, tearing down the Upstairs + // (which deactivates the Volume) // // - reconciliation could still be going on // // - reconciliation could have failed // - // If it's not active, wait until the next invocation of this saga - // to decide what to do next. + // If not, wait until the next invocation of this saga to decide + // what to do next. - result.into_inner().active + volume_info_reconciled(&info) } ReplaceResult::Missing => { diff --git a/nexus/test-utils/src/nexus_test.rs b/nexus/test-utils/src/nexus_test.rs index 693aea88732..48c945e742b 100644 --- a/nexus/test-utils/src/nexus_test.rs +++ b/nexus/test-utils/src/nexus_test.rs @@ -117,6 +117,7 @@ pub struct ControlPlaneTestContext { /// Ports of stopped dendrite instances (for use by start_dendrite) pub stopped_dendrite_ports: RwLock>, pub mgd: HashMap, + pub ddm: HashMap, pub external_dns_zone_name: String, pub external_dns: TransientDnsServer, pub internal_dns: TransientDnsServer, @@ -320,6 +321,9 @@ impl ControlPlaneTestContext { for (_, mut mgd) in self.mgd { mgd.cleanup().await.unwrap(); } + for (_, mut ddm) in self.ddm { + ddm.cleanup().await.unwrap(); + } self.logctx.cleanup_successful(); } } diff --git a/nexus/test-utils/src/resource_helpers.rs b/nexus/test-utils/src/resource_helpers.rs index ace0d2f265a..2cf4c62bad3 100644 --- a/nexus/test-utils/src/resource_helpers.rs +++ b/nexus/test-utils/src/resource_helpers.rs @@ -233,6 +233,29 @@ where .unwrap() } +/// Like [`object_put`], but expects 201 Created instead of 200 OK. +pub async fn object_put_upsert( + client: &ClientTestContext, + path: &str, + input: &InputType, +) -> OutputType +where + InputType: serde::Serialize, + OutputType: serde::de::DeserializeOwned, +{ + NexusRequest::new( + RequestBuilder::new(client, Method::PUT, path) + .body(Some(input)) + .expect_status(Some(StatusCode::CREATED)), + ) + .authn_as(AuthnMode::PrivilegedUser) + .execute() + .await + .unwrap_or_else(|e| panic!("failed to make \"PUT\" request to {path}: {e}")) + .parsed_body() + .unwrap() +} + pub async fn object_put_error( client: &ClientTestContext, path: &str, diff --git a/nexus/test-utils/src/starter.rs b/nexus/test-utils/src/starter.rs index 3822be75d1f..bb84aed361c 100644 --- a/nexus/test-utils/src/starter.rs +++ b/nexus/test-utils/src/starter.rs @@ -23,6 +23,7 @@ use futures::future::BoxFuture; use gateway_test_utils::setup::GatewayTestContext; use iddqd::IdOrdMap; use internal_dns_types::config::DnsConfigBuilder; +use internal_dns_types::config::HostSwitchZonePorts; use internal_dns_types::names::DNS_ZONE_EXTERNAL_TESTING; use internal_dns_types::names::ServiceName; use nexus_config::Database; @@ -146,6 +147,7 @@ pub struct ControlPlaneStarter<'a, N: NexusServer> { pub gateway: BTreeMap, pub dendrite: RwLock>, pub mgd: HashMap, + pub ddm: HashMap, // NOTE: Only exists after starting Nexus, until external Nexus is // initialized. @@ -203,6 +205,7 @@ impl<'a, N: NexusServer> ControlPlaneStarter<'a, N> { gateway: BTreeMap::new(), dendrite: RwLock::new(HashMap::new()), mgd: HashMap::new(), + ddm: HashMap::new(), nexus_internal: None, nexus_internal_addr: None, external_dns_zone_name: None, @@ -465,6 +468,17 @@ impl<'a, N: NexusServer> ControlPlaneStarter<'a, N> { self.config.pkg.mgd.insert(switch_slot, config); } + pub async fn start_ddm(&mut self, switch_slot: SwitchSlot) { + let log = &self.logctx.log; + debug!(log, "Starting DDM sim"; "switch_slot" => ?switch_slot); + + let ddm = dev::maghemite::DdmInstance::start().await.unwrap(); + let port = ddm.port; + self.ddm.insert(switch_slot, ddm); + + debug!(log, "DDM sim started"; "port" => port); + } + pub async fn record_switch_dns( &mut self, sled_id: SledUuid, @@ -483,9 +497,18 @@ impl<'a, N: NexusServer> ControlPlaneStarter<'a, N> { .host_zone_switch( sled_id, Ipv6Addr::LOCALHOST, - self.dendrite.read().unwrap().get(&switch_slot).unwrap().port, - self.gateway.get(&switch_slot).unwrap().port, - self.mgd.get(&switch_slot).unwrap().port, + HostSwitchZonePorts { + dendrite: self + .dendrite + .read() + .unwrap() + .get(&switch_slot) + .unwrap() + .port, + mgs: self.gateway.get(&switch_slot).unwrap().port, + mgd: self.mgd.get(&switch_slot).unwrap().port, + ddm: self.ddm.get(&switch_slot).unwrap().port, + }, ) .unwrap() } @@ -1254,6 +1277,7 @@ impl<'a, N: NexusServer> ControlPlaneStarter<'a, N> { dendrite: RwLock::new(self.dendrite.into_inner().unwrap()), stopped_dendrite_ports: RwLock::new(HashMap::new()), mgd: self.mgd, + ddm: self.ddm, external_dns_zone_name: self.external_dns_zone_name.unwrap(), external_dns: self.external_dns.unwrap(), internal_dns: self.internal_dns.unwrap(), @@ -1295,6 +1319,9 @@ impl<'a, N: NexusServer> ControlPlaneStarter<'a, N> { for (_, mut mgd) in self.mgd { mgd.cleanup().await.unwrap(); } + for (_, mut ddm) in self.ddm { + ddm.cleanup().await.unwrap(); + } self.logctx.cleanup_successful(); } @@ -1635,6 +1662,12 @@ pub(crate) async fn setup_with_config_impl( builder.start_mgd(SwitchSlot::Switch0).boxed() }), ), + ( + "start_ddm_switch0", + Box::new(|builder| { + builder.start_ddm(SwitchSlot::Switch0).boxed() + }), + ), ( "record_switch_dns", Box::new(|builder| { @@ -1679,6 +1712,12 @@ pub(crate) async fn setup_with_config_impl( builder.start_mgd(SwitchSlot::Switch1).boxed() }), ), + ( + "start_ddm_switch1", + Box::new(|builder| { + builder.start_ddm(SwitchSlot::Switch1).boxed() + }), + ), ( "record_switch_dns", Box::new(|builder| { diff --git a/nexus/tests/integration_tests/initialization.rs b/nexus/tests/integration_tests/initialization.rs index 350757cf1de..714880feb37 100644 --- a/nexus/tests/integration_tests/initialization.rs +++ b/nexus/tests/integration_tests/initialization.rs @@ -158,6 +158,11 @@ async fn test_nexus_boots_before_dendrite() { starter.start_mgd(SwitchSlot::Switch1).await; info!(log, "Started mgd"); + info!(log, "Starting ddm"); + starter.start_ddm(SwitchSlot::Switch0).await; + starter.start_ddm(SwitchSlot::Switch1).await; + info!(log, "Started ddm"); + info!(log, "Populating internal DNS records"); starter .record_switch_dns( @@ -197,6 +202,8 @@ async fn nexus_schema_test_setup( starter.start_dendrite(SwitchSlot::Switch1).await; starter.start_mgd(SwitchSlot::Switch0).await; starter.start_mgd(SwitchSlot::Switch1).await; + starter.start_ddm(SwitchSlot::Switch0).await; + starter.start_ddm(SwitchSlot::Switch1).await; starter.populate_internal_dns().await; } diff --git a/nexus/tests/integration_tests/instances.rs b/nexus/tests/integration_tests/instances.rs index 2aff4004804..f28e401c05d 100644 --- a/nexus/tests/integration_tests/instances.rs +++ b/nexus/tests/integration_tests/instances.rs @@ -9009,8 +9009,6 @@ pub async fn instance_simulate(nexus: &Arc, id: &InstanceUuid) { /// /// Returns an error instead of panicking if the sled agent communication fails. /// This is useful during test cleanup where the sled agent may be unavailable. -// This is currently only consumed by tests behind the multicast feature gate. -// If/when it has another consumer, this cfg can be removed. #[cfg(feature = "multicast")] pub async fn try_instance_simulate( nexus: &Arc, diff --git a/nexus/tests/integration_tests/multicast/failures.rs b/nexus/tests/integration_tests/multicast/failures.rs index eef03cead71..a0f70b79320 100644 --- a/nexus/tests/integration_tests/multicast/failures.rs +++ b/nexus/tests/integration_tests/multicast/failures.rs @@ -1044,15 +1044,14 @@ async fn test_left_member_waits_for_group_active( /// Test underlay IP collision detection and salt-based retry mechanism. /// -/// This test verifies that when two external groups would map to the same -/// underlay IP, the reconciler detects the collision and retries with an +/// Verifies that when two external groups would map to the same underlay +/// IP, the reconciler detects the collision and retries with an /// incremented salt value. /// -/// Test setup: -/// 1. Create group A with external IP 224.5.5.5 (via instance join) -/// 2. Pre-occupy B's target underlay IP (ff04::e001:0203) using A's tag -/// 3. Create group B with external IP 224.1.2.3 (maps to ff04::e001:0203 with salt=0) -/// 4. When reconciler processes B, it hits collision and retries with salt=1 +/// Steps: a) create group A (224.5.5.5), let it reach Active, b) stop +/// DPD and pre-occupy B's target underlay (ff04::e001:0203) with A's +/// tag, c) create group B (224.1.2.3), d) restart DPD and reconcile, +/// e) verify B has salt > 0 and A/B have different underlays. #[nexus_test] async fn test_multicast_group_underlay_collision_retry( cptestctx: &ControlPlaneTestContext, @@ -1066,11 +1065,10 @@ async fn test_multicast_group_underlay_collision_retry( let project_name = "collision-test-project"; let instance_a_name = "collision-instance-a"; let instance_b_name = "collision-instance-b"; - // Use IP addresses as group identifiers for explicit IP allocation let group_a_ip = "224.5.5.5"; - let group_b_ip = "224.1.2.3"; // Known mapping: → ff04::e001:0203 with salt=0 + let group_b_ip = "224.1.2.3"; // Maps to ff04::e001:0203 at salt=0 - // Setup: project, pools + // Setup: project, pools, instances ops::join3( create_project(&client, project_name), create_default_ip_pools(&client), @@ -1083,17 +1081,14 @@ async fn test_multicast_group_underlay_collision_retry( ) .await; - // Create two instances for membership ops::join2( create_instance(client, project_name, instance_a_name), create_instance(client, project_name, instance_b_name), ) .await; - // Stop DPD to control when groups transition to "Active" - cptestctx.stop_dendrite(SwitchSlot::Switch0).await; - - // Create group A by joining instance to IP address (implicit group creation) + // Step 1: let group A reach Active so its underlay is deterministic + // before setting up the collision. multicast_group_attach( cptestctx, project_name, @@ -1102,18 +1097,34 @@ async fn test_multicast_group_underlay_collision_retry( ) .await; - // Fetch group A (created by attach) from API using IP address as identifier let group_a = get_multicast_group(client, group_a_ip).await; + wait_for_group_active(client, group_a_ip).await; + let group_a_model = datastore .multicast_group_fetch( &opctx, MulticastGroupUuid::from_untyped_uuid(group_a.identity.id), ) .await - .expect("Should fetch group A"); + .expect("Should fetch group A after Active"); + let underlay_a = datastore + .underlay_multicast_group_fetch( + &opctx, + group_a_model.underlay_group_id.unwrap(), + ) + .await + .expect("Should fetch A's underlay group"); + + assert_eq!( + underlay_a.multicast_ip.ip().to_string(), + "ff04::e005:505", + "A's underlay should be its natural mapping (224.5.5.5)" + ); + + // Step 2: stop DPD and pre-occupy B's target underlay IP using A's + // tag so B will collide at salt=0. + cptestctx.stop_dendrite(SwitchSlot::Switch0).await; - // Pre-occupy B's target underlay IP by creating an underlay group with A's tag - // B's target: ff04::e001:0203 (known mapping for 224.1.2.3 with salt=0) let collision_ip: ipnetwork::IpNetwork = "ff04::e001:0203".parse().unwrap(); let res = datastore .ensure_underlay_multicast_group( @@ -1124,7 +1135,6 @@ async fn test_multicast_group_underlay_collision_retry( .await .expect("Should create collision underlay"); - // Verify the underlay was created (for group A, but with B's target IP) assert!( matches!( res, @@ -1133,7 +1143,7 @@ async fn test_multicast_group_underlay_collision_retry( "Should have created underlay group with collision IP" ); - // Create group B by joining instance to IP address (implicit group creation) + // Step 3: create group B. multicast_group_attach( cptestctx, project_name, @@ -1142,14 +1152,15 @@ async fn test_multicast_group_underlay_collision_retry( ) .await; - // Fetch group B (created by attach) from API using IP address as identifier let group_b = get_multicast_group(client, group_b_ip).await; - // Restart DPD and run reconciler, triggering collision detection + // Step 4: restart DPD and run the reconciler. cptestctx.restart_dendrite(SwitchSlot::Switch0).await; activate_multicast_reconciler(&cptestctx.lockstep_client).await; - // Fetch group B after reconciliation + // Step 5: verify collision was handled correctly. + wait_for_group_active(client, group_b_ip).await; + let group_b_after = datastore .multicast_group_fetch( &opctx, @@ -1158,7 +1169,6 @@ async fn test_multicast_group_underlay_collision_retry( .await .expect("Should fetch group B after reconciliation"); - // Verify B got a salt value > 0 (collision was detected and retried) assert!( group_b_after.underlay_salt.is_some(), "Group B should have a salt value after collision retry" @@ -1169,36 +1179,32 @@ async fn test_multicast_group_underlay_collision_retry( "Salt should be > 0 after collision (got {})", *salt_value ); - - // Verify B got an underlay group (different from the collision IP) assert!( group_b_after.underlay_group_id.is_some(), "Group B should have an underlay group after collision retry" ); - // Fetch A's underlay group and verify it has the collision IP - let group_a_after = datastore + // Verify A and B both got underlay groups and they differ. + let group_a_final = datastore .multicast_group_fetch( &opctx, MulticastGroupUuid::from_untyped_uuid(group_a.identity.id), ) .await - .expect("Should fetch group A after reconciliation"); + .expect("Should fetch group A after full reconciliation"); + assert!( + group_a_final.underlay_group_id.is_some(), + "Group A should have an underlay group" + ); + let underlay_a = datastore .underlay_multicast_group_fetch( &opctx, - group_a_after.underlay_group_id.unwrap(), + group_a_final.underlay_group_id.unwrap(), ) .await .expect("Should fetch A's underlay group"); - assert_eq!( - underlay_a.multicast_ip.ip().to_string(), - "ff04::e001:203", - "A's underlay IP should be the collision IP we set" - ); - - // Fetch B's underlay group and verify it has a different IP than the collision IP let underlay_b = datastore .underlay_multicast_group_fetch( &opctx, @@ -1207,13 +1213,6 @@ async fn test_multicast_group_underlay_collision_retry( .await .expect("Should fetch B's underlay group"); - assert_ne!( - underlay_b.multicast_ip.ip().to_string(), - "ff04::e001:203", - "B's underlay IP should differ from collision IP due to salt" - ); - - // Verify A and B have different underlay IPs assert_ne!( underlay_a.multicast_ip, underlay_b.multicast_ip, "A and B should have different underlay IPs" diff --git a/nexus/types/src/deployment/execution/dns.rs b/nexus/types/src/deployment/execution/dns.rs index 009377fd8d9..c901dcc92f7 100644 --- a/nexus/types/src/deployment/execution/dns.rs +++ b/nexus/types/src/deployment/execution/dns.rs @@ -155,9 +155,7 @@ pub fn blueprint_internal_dns_config( dns_builder.host_zone_switch( scrimlet.id(), switch_zone_ip, - overrides.dendrite_port(scrimlet.id()), - overrides.mgs_port(scrimlet.id()), - overrides.mgd_port(scrimlet.id()), + overrides.host_switch_zone_ports(scrimlet.id()), )?; } diff --git a/nexus/types/src/deployment/execution/overridables.rs b/nexus/types/src/deployment/execution/overridables.rs index 881a7c49bdd..bf46374d1dc 100644 --- a/nexus/types/src/deployment/execution/overridables.rs +++ b/nexus/types/src/deployment/execution/overridables.rs @@ -2,6 +2,8 @@ // 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 internal_dns_types::config::HostSwitchZonePorts; +use omicron_common::address::DDMD_PORT; use omicron_common::address::DENDRITE_PORT; use omicron_common::address::Ipv6Subnet; use omicron_common::address::MGD_PORT; @@ -29,6 +31,8 @@ pub struct Overridables { pub mgs_ports: BTreeMap, /// map: sled id -> TCP port on which that sled's MGD is listening pub mgd_ports: BTreeMap, + /// map: sled id -> TCP port on which that sled's DDM is listening + pub ddm_ports: BTreeMap, /// map: sled id -> IP address of the sled's switch zone pub switch_zone_ips: BTreeMap, } @@ -67,6 +71,32 @@ impl Overridables { self.mgd_ports.get(&sled_id).copied().unwrap_or(MGD_PORT) } + /// Specify the TCP port on which this sled's DDM is listening + pub fn override_ddm_port(&mut self, sled_id: SledUuid, port: u16) { + self.ddm_ports.insert(sled_id, port); + } + + /// Returns the TCP port on which this sled's DDM is listening + pub fn ddm_port(&self, sled_id: SledUuid) -> u16 { + self.ddm_ports.get(&sled_id).copied().unwrap_or(DDMD_PORT) + } + + /// Returns the per-switch-zone service ports for this sled. + /// + /// Bundles the four switch-zone admin ports into a single + /// [`HostSwitchZonePorts`] so callers cannot swap fields by accident. + pub fn host_switch_zone_ports( + &self, + sled_id: SledUuid, + ) -> HostSwitchZonePorts { + HostSwitchZonePorts { + dendrite: self.dendrite_port(sled_id), + mgs: self.mgs_port(sled_id), + mgd: self.mgd_port(sled_id), + ddm: self.ddm_port(sled_id), + } + } + /// Specify the IP address of this switch zone pub fn override_switch_zone_ip( &mut self, diff --git a/nexus/types/versions/Cargo.toml b/nexus/types/versions/Cargo.toml index 6c0a2274c6e..ddffa6b880c 100644 --- a/nexus/types/versions/Cargo.toml +++ b/nexus/types/versions/Cargo.toml @@ -16,6 +16,7 @@ daft.workspace = true dropshot.workspace = true http.workspace = true mg-admin-client.workspace = true +mg-api-types.workspace = true omicron-common.workspace = true omicron-passwords.workspace = true omicron-uuid-kinds.workspace = true diff --git a/nexus/types/versions/src/impls/networking.rs b/nexus/types/versions/src/impls/networking.rs index 0831b35c920..ddbcec8479f 100644 --- a/nexus/types/versions/src/impls/networking.rs +++ b/nexus/types/versions/src/impls/networking.rs @@ -21,11 +21,11 @@ impl From for latest::networking::AddressLotBlockCreate { } } -impl From +impl From for latest::networking::BgpPeerState { - fn from(s: mg_admin_client::types::FsmStateKind) -> Self { - use mg_admin_client::types::FsmStateKind; + fn from(s: mg_api_types::bgp::session::FsmStateKind) -> Self { + use mg_api_types::bgp::session::FsmStateKind; match s { FsmStateKind::Idle => Self::Idle, FsmStateKind::Connect => Self::Connect, diff --git a/package-manifest.toml b/package-manifest.toml index 63dad9bbcd8..f1080f0820c 100644 --- a/package-manifest.toml +++ b/package-manifest.toml @@ -624,10 +624,10 @@ only_for_targets.image = "standard" # 3. Use source.type = "manual" instead of "prebuilt" source.type = "prebuilt" source.repo = "crucible" -source.commit = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source.commit = "3c1708d86e10f0370807388a1efe092edd99d431" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/crucible/image//crucible.sha256.txt -source.sha256 = "8e245572e4b8d1c018884268a6afdf7f79efc22e61b4ed5b5526957bf61ccdcd" +source.sha256 = "5c5bc85240acf5fbc8528cfd7930a025d9e00c08bf890df652b3aaee796e8093" output.type = "zone" output.intermediate_only = true @@ -636,10 +636,10 @@ service_name = "crucible_pantry_prebuilt" only_for_targets.image = "standard" source.type = "prebuilt" source.repo = "crucible" -source.commit = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source.commit = "3c1708d86e10f0370807388a1efe092edd99d431" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/crucible/image//crucible-pantry.sha256.txt -source.sha256 = "7998ddb0bda4c97e3d5fec7c8079bbdfb27ef06dba69ab2867278dc2cd7544f4" +source.sha256 = "9bdeddff8d1c2133c6be238b35a498138d7bcecb26df7820f5d50cde781dac7a" output.type = "zone" output.intermediate_only = true @@ -653,10 +653,10 @@ service_name = "crucible_utils" only_for_targets.image = "standard" source.type = "prebuilt" source.repo = "crucible" -source.commit = "7103cd3a3d7b0112d2949dd135db06fef0c156bb" +source.commit = "3c1708d86e10f0370807388a1efe092edd99d431" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/crucible/image//crucible-utils.sha256.txt -source.sha256 = "cc661b84fd258467ec1961e8c9879f76d2d07903fb9161012afa75c37490e24f" +source.sha256 = "13dfdf6272fbe129b7b608b248cc017f2cc3802b41f50755038669f5eb6b2c96" output.type = "tarball" # Refer to @@ -667,10 +667,10 @@ service_name = "propolis-server" only_for_targets.image = "standard" source.type = "prebuilt" source.repo = "propolis" -source.commit = "bc489ddf0f38f75e0c194b86cf6f0de377f68845" +source.commit = "75518ad2c26db9278895ab1262c28daf13bfdffd" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/propolis/image//propolis-server.sha256.txt -source.sha256 = "f4c742c766a7260f20d6ef3c0d68c362e7ba768037836bfa09abdf1c3c72e605" +source.sha256 = "5dd6af4c615e471cde8df42c0a9152e879e552ad248910d8e74c007b6bc028c7" output.type = "zone" [package.mg-ddm-gz] @@ -683,10 +683,10 @@ source.repo = "maghemite" # `tools/maghemite_openapi_version`. Failing to do so will cause a failure when # building `ddm-admin-client` (which will instruct you to update # `tools/maghemite_openapi_version`). -source.commit = "7696ee48d5ee29a917dea459e281fe2e8ff20513" +source.commit = "5060d5864df864741dac8a0a4078fa4ecc3c73c8" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/maghemite/image//mg-ddm-gz.sha256.txt -source.sha256 = "ce52b9094adf0ed567bd3ed1e3ac48ac1c983cc7859adacf4f392e415a1189ad" +source.sha256 = "b37a117496ffcee4633d96c1daaec2c0584968573ba3989915b968caccc92007" output.type = "tarball" [package.mg-ddm] @@ -699,10 +699,10 @@ source.repo = "maghemite" # `tools/maghemite_openapi_version`. Failing to do so will cause a failure when # building `ddm-admin-client` (which will instruct you to update # `tools/maghemite_openapi_version`). -source.commit = "7696ee48d5ee29a917dea459e281fe2e8ff20513" +source.commit = "5060d5864df864741dac8a0a4078fa4ecc3c73c8" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/maghemite/image//mg-ddm.sha256.txt -source.sha256 = "23950a4e73a07fa7f087ba3312e4bc5a8981fd9ebad54af2350baaa86ad6bbf3" +source.sha256 = "7ab1f395b5bbe257a7533642b237706503e1aee6df84832f9e8ae840096be79c" output.type = "zone" output.intermediate_only = true @@ -714,10 +714,10 @@ source.repo = "maghemite" # `tools/maghemite_openapi_version`. Failing to do so will cause a failure when # building `ddm-admin-client` (which will instruct you to update # `tools/maghemite_openapi_version`). -source.commit = "7696ee48d5ee29a917dea459e281fe2e8ff20513" +source.commit = "5060d5864df864741dac8a0a4078fa4ecc3c73c8" # The SHA256 digest is automatically posted to: # https://buildomat.eng.oxide.computer/public/file/oxidecomputer/maghemite/image//mgd.sha256.txt -source.sha256 = "301d31ca481e4822f69484feacca31dd08a7c4aae87d96641d384bda3178d2f3" +source.sha256 = "96f75128b9c6f836a8c9e55ff06272cfc342f55b3c817bcc9d1e04b0c1f4eb80" output.type = "zone" output.intermediate_only = true @@ -725,8 +725,8 @@ output.intermediate_only = true service_name = "lldp" source.type = "prebuilt" source.repo = "lldp" -source.commit = "61479b6922f9112fbe1e722414d2b8055212cb12" -source.sha256 = "8f988c0b0fa3ad4121ab0e825298601035e56c5c054bdc3a1dfb4d6c8fd5b300" +source.commit = "0d199cebf8fe3b24b9d943bdc46a00c227e5a345" +source.sha256 = "4078931e1cccec31e51386c14d08641110d042686074c649d4bbcf8e90658bdb" output.type = "zone" output.intermediate_only = true @@ -765,8 +765,8 @@ only_for_targets.image = "standard" # the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" -source.commit = "50ed531201ea5f85115141a2203d551fbbdaa3da" -source.sha256 = "5dab6b11e339db1c82f9e994935eee4b3c22bbc37cf76f5cebd46198b32d8de9" +source.commit = "90efdc6cf618523320bc403f1b9484dbd7f762be" +source.sha256 = "15eed4d125f1088d9a531d0bf812a8217ba1d9daa21d8ae986a5635a90ef951e" output.type = "zone" output.intermediate_only = true @@ -792,8 +792,8 @@ only_for_targets.image = "standard" # the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" -source.commit = "50ed531201ea5f85115141a2203d551fbbdaa3da" -source.sha256 = "401557bc5b85691e8785a8a3c16df14b478c4f7f718017f9fb8a33c013b4479a" +source.commit = "90efdc6cf618523320bc403f1b9484dbd7f762be" +source.sha256 = "d83419114bbf4eb2a18f91ad67ee9dc5e6e4154159aece7b1b274bdc5d938c4e" output.type = "zone" output.intermediate_only = true @@ -812,8 +812,8 @@ only_for_targets.image = "standard" # the other `source.*` keys. source.type = "prebuilt" source.repo = "dendrite" -source.commit = "50ed531201ea5f85115141a2203d551fbbdaa3da" -source.sha256 = "a047b4f9345a816521b6a1204c3ee11f91994614d798051a39204137a11805fc" +source.commit = "90efdc6cf618523320bc403f1b9484dbd7f762be" +source.sha256 = "d5f5f24475ea191a057c63ca388366bc77d6e0e126968028b6384b2bffa45c57" output.type = "zone" output.intermediate_only = true @@ -833,8 +833,8 @@ setup_hint = "Run `cargo xtask download transceiver-control` to download the nec service_name = "thundermuffin" source.type = "prebuilt" source.repo = "thundermuffin" -source.commit = "a4a6108d7c9aac2464a0b6898e88132a8f701a13" -source.sha256 = "dc55a2accd33a347df4cbdc0026cbaccea2c004940c3fec8cadcdd633d440dfa" +source.commit = "473a90f8081e99f1b77f2625d97ab1f7817b3468" +source.sha256 = "757600f364dcffa2ffccf6a95918b99b4dbd71c8ec60746a776143f3e086b519" output.type = "zone" output.intermediate_only = true diff --git a/sled-agent/Cargo.toml b/sled-agent/Cargo.toml index a51f0579054..a0556cc1f5b 100644 --- a/sled-agent/Cargo.toml +++ b/sled-agent/Cargo.toml @@ -32,6 +32,7 @@ clap.workspace = true clickhouse-admin-types.workspace = true # Only used by the simulated sled agent. crucible-agent-client.workspace = true +crucible-pantry-client.workspace = true derive_more.workspace = true dice-verifier = { workspace = true, features = ["ipcc", "mock"] } display-error-chain.workspace = true @@ -83,7 +84,6 @@ range-requests.workspace = true repo-depot-api.workspace = true repo-depot-client.workspace = true reqwest = { workspace = true, features = ["rustls", "stream"] } -reqwest012 = { workspace = true } schemars = { workspace = true, features = ["chrono", "uuid1"] } serde.workspace = true serde_json = { workspace = true, features = ["raw_value"] } diff --git a/sled-agent/early-networking/Cargo.toml b/sled-agent/early-networking/Cargo.toml index 0ae4cc1428c..8fb7b9e21f7 100644 --- a/sled-agent/early-networking/Cargo.toml +++ b/sled-agent/early-networking/Cargo.toml @@ -19,11 +19,11 @@ http.workspace = true internal-dns-resolver.workspace = true internal-dns-types.workspace = true mg-admin-client.workspace = true +mg-api-types.workspace = true omicron-common.workspace = true omicron-ddm-admin-client.workspace = true omicron-workspace-hack.workspace = true oxnet.workspace = true -rdb-types.workspace = true sled-agent-types.workspace = true slog.workspace = true slog-error-chain.workspace = true diff --git a/sled-agent/early-networking/src/lib.rs b/sled-agent/early-networking/src/lib.rs index b4e9e6f768b..edf0bf400a0 100644 --- a/sled-agent/early-networking/src/lib.rs +++ b/sled-agent/early-networking/src/lib.rs @@ -16,18 +16,23 @@ use internal_dns_resolver::{ResolveError, Resolver as DnsResolver}; use internal_dns_types::names::ServiceName; use mg_admin_client::Client as MgdClient; use mg_admin_client::types::{ - AddStaticRoute4Request, AddStaticRoute6Request, ApplyRequest, - BestpathFanoutRequest, CheckerSource, - ImportExportPolicy4 as MgImportExportPolicy4, - ImportExportPolicy6 as MgImportExportPolicy6, JitterRange, ShaperSource, - StaticRoute4, StaticRoute4List, StaticRoute6, StaticRoute6List, + ApplyRequest, BfdPeerConfig as MgBfdPeerConfig, + BgpPeerConfig as MgBgpPeerConfig, + UnnumberedBgpPeerConfig as MgUnnumberedBgpPeerConfig, }; -use mg_admin_client::types::{ - BfdPeerConfig as MgBfdPeerConfig, Ipv4UnicastConfig, +use mg_api_types::bgp::config::{ + CheckerSource, Ipv4UnicastConfig, Ipv6UnicastConfig, JitterRange, + ShaperSource, }; -use mg_admin_client::types::{ - BgpPeerConfig as MgBgpPeerConfig, Ipv6UnicastConfig, - UnnumberedBgpPeerConfig as MgUnnumberedBgpPeerConfig, +use mg_api_types::bgp::policy::{ + ImportExportPolicy4 as MgImportExportPolicy4, + ImportExportPolicy6 as MgImportExportPolicy6, +}; +use mg_api_types::rdb::prefix::{Prefix, Prefix4, Prefix6}; +use mg_api_types::rib::BestpathFanoutRequest; +use mg_api_types::static_routes::{ + AddStaticRoute4Request, AddStaticRoute6Request, StaticRoute4, + StaticRoute4List, StaticRoute6, StaticRoute6List, }; use omicron_common::OMICRON_DPD_TAG; use omicron_common::address::DENDRITE_PORT; @@ -37,7 +42,6 @@ use omicron_common::backoff::{ }; use omicron_ddm_admin_client::DdmError; use oxnet::IpNet; -use rdb_types::{Prefix, Prefix4, Prefix6}; use sled_agent_types::early_networking::{ BfdMode, BgpConfig, BgpPeerConfig, ImportExportPolicy, LinkFec, LinkSpeed, PortConfig, RouterPeerType, SwitchSlot, UplinkAddress, @@ -712,7 +716,7 @@ impl<'a> EarlyNetworkSetup<'a> { fanout: config.max_paths.as_nonzero_u8(), }; - if let Err(e) = mgd.bgp_apply_v2(&request).await { + if let Err(e) = mgd.bgp_apply(&request).await { error!( self.log, "BGP peer configuration failed"; diff --git a/sled-agent/rack-setup/src/plan/service.rs b/sled-agent/rack-setup/src/plan/service.rs index 48de2b1d3e9..6ec735e8f62 100644 --- a/sled-agent/rack-setup/src/plan/service.rs +++ b/sled-agent/rack-setup/src/plan/service.rs @@ -13,7 +13,7 @@ use iddqd::errors::DuplicateItem; use iddqd::id_upcast; use illumos_utils::zpool::ZpoolName; use internal_dns_types::config::{ - DnsConfigBuilder, DnsConfigParams, Host, Zone, + DnsConfigBuilder, DnsConfigParams, Host, HostSwitchZonePorts, Zone, }; use internal_dns_types::names::ServiceName; use nexus_types::deployment::LastAllocatedSubnetIpOffset; @@ -29,10 +29,10 @@ use nexus_types::deployment::{ }; use nexus_types::external_api::sled::SledState; use omicron_common::address::{ - CP_SERVICES_RESERVED_ADDRESSES, DENDRITE_PORT, DNS_HTTP_PORT, DNS_PORT, - Ipv6Subnet, MGD_PORT, MGS_PORT, NEXUS_INTERNAL_PORT, NEXUS_LOCKSTEP_PORT, - NTP_PORT, NUM_SOURCE_NAT_PORTS, REPO_DEPOT_PORT, ReservedRackSubnet, - SLED_PREFIX, SLED_RESERVED_ADDRESSES, get_sled_address, + CP_SERVICES_RESERVED_ADDRESSES, DDMD_PORT, DENDRITE_PORT, DNS_HTTP_PORT, + DNS_PORT, Ipv6Subnet, MGD_PORT, MGS_PORT, NEXUS_INTERNAL_PORT, + NEXUS_LOCKSTEP_PORT, NTP_PORT, NUM_SOURCE_NAT_PORTS, REPO_DEPOT_PORT, + ReservedRackSubnet, SLED_PREFIX, SLED_RESERVED_ADDRESSES, get_sled_address, get_switch_zone_address, }; use omicron_common::api::external::{Generation, MacAddr, Vni}; @@ -338,9 +338,12 @@ impl ServicePlan { .host_zone_switch( sled.sled_id, address, - DENDRITE_PORT, - MGS_PORT, - MGD_PORT, + HostSwitchZonePorts { + dendrite: DENDRITE_PORT, + mgs: MGS_PORT, + mgd: MGD_PORT, + ddm: DDMD_PORT, + }, ) .unwrap(); } diff --git a/sled-agent/src/sim/collection.rs b/sled-agent/src/sim/collection.rs index 56bcb3aaf3d..84a31190cb8 100644 --- a/sled-agent/src/sim/collection.rs +++ b/sled-agent/src/sim/collection.rs @@ -245,7 +245,11 @@ impl SimCollection { // particular, we want to finish this work before calling out to // notify the nexus. let mut objects = self.objects.lock().await; - let mut object = objects.remove(&id).unwrap(); + let Some(mut object) = objects.remove(&id) else { + // Instance was already removed (e.g., destroyed by a + // concurrent transition). Nothing left to do. + break; + }; object.transition_finish(); let after = object.object.current().clone(); diff --git a/sled-agent/src/sim/http_entrypoints_pantry.rs b/sled-agent/src/sim/http_entrypoints_pantry.rs index 231593c74af..ee5e4bd9d38 100644 --- a/sled-agent/src/sim/http_entrypoints_pantry.rs +++ b/sled-agent/src/sim/http_entrypoints_pantry.rs @@ -3,7 +3,13 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. //! HTTP entrypoint functions for simulating the crucible pantry API. +//! +//! `VolumeStatus` and its tree (`VolumeInfo`, `UpstairsInfoStatus`, etc.) are +//! imported directly from `crucible_pantry_client::types` so the simulator's +//! OpenAPI stays byte-identical to the real pantry's, since both are generated +//! from the same upstream OpenAPI document. +use crucible_pantry_client::types::VolumeStatus; use dropshot::{ ApiDescription, ApiDescriptionRegisterError, HttpError, HttpResponseDeleted, HttpResponseOk, HttpResponseUpdatedNoContent, @@ -45,10 +51,6 @@ pub fn api() -> CruciblePantryApiDescription { api } -// TODO: We'd like to de-duplicate as much as possible with the real crucible -// pantry here, to avoid skew. However, this was wholesale copied from the -// crucible repo! - #[derive(Serialize, JsonSchema)] pub struct PantryStatus { /// Which volumes does this Pantry know about? Note this may include volumes @@ -79,18 +81,6 @@ struct VolumePath { pub id: String, } -#[derive(Clone, Deserialize, Serialize, JsonSchema)] -pub struct VolumeStatus { - /// Is the Volume currently active? - pub active: bool, - - /// Has the Pantry ever seen this Volume active? - pub seen_active: bool, - - /// How many job handles are there for this Volume? - pub num_job_handles: usize, -} - /// Get a current Volume's status #[endpoint { method = GET, @@ -488,6 +478,14 @@ mod tests { if key == "description" && value.is_string() { continue; } + // Also skip "format" string differences: progenitor's + // round-trip (OpenAPI -> Rust type -> schemars) loses + // integer-format precision (e.g. real's `usize` -> `uint` + // becomes generated `u32` -> `uint32`). These hints don't + // affect schema semantics. + if key == "format" && value.is_string() { + continue; + } let new_path = format!("{path}/{key}"); let rhs_value = rhs_map.get(key).unwrap_or_else(|| { panic!("Real API JSON missing key: \"{new_path}\"") diff --git a/sled-agent/src/sim/storage.rs b/sled-agent/src/sim/storage.rs index 7e2bfacfe5b..255f935ae4d 100644 --- a/sled-agent/src/sim/storage.rs +++ b/sled-agent/src/sim/storage.rs @@ -11,7 +11,6 @@ use crate::sim::SimulatedUpstairs; use crate::sim::http_entrypoints_pantry::ExpectedDigest; use crate::sim::http_entrypoints_pantry::PantryStatus; -use crate::sim::http_entrypoints_pantry::VolumeStatus; use crate::support_bundle::storage::SupportBundleManager; use anyhow::{self, Result, bail}; use camino::Utf8Path; @@ -20,6 +19,8 @@ use chrono::prelude::*; use crucible_agent_client::types::{ CreateRegion, Region, RegionId, RunningSnapshot, Snapshot, State, }; +use crucible_pantry_client::types::VolumeInfo; +use crucible_pantry_client::types::VolumeStatus; use dropshot::HandlerTaskMode; use dropshot::HttpError; use illumos_utils::zfs::DatasetProperties; @@ -2067,6 +2068,10 @@ impl Pantry { active: true, seen_active: true, num_job_handles: 0, + info: VolumeInfo::Volume { + read_only_parent: None, + sub_volumes: vec![], + }, }, activate_job: None, }, @@ -2097,6 +2102,10 @@ impl Pantry { active: auto_activate_volumes, seen_active: auto_activate_volumes, num_job_handles: 1, + info: VolumeInfo::Volume { + read_only_parent: None, + sub_volumes: vec![], + }, }, activate_job: Some(activate_job_id.clone()), }, diff --git a/test-utils/src/dev/maghemite.rs b/test-utils/src/dev/maghemite.rs index eaae1af8cd4..a7091d38508 100644 --- a/test-utils/src/dev/maghemite.rs +++ b/test-utils/src/dev/maghemite.rs @@ -135,11 +135,123 @@ fn redirect_file( .with_context(|| format!("open \"{}\"", out_path.display())) } +/// Per-OS configuration for asking the kernel which TCP port a pid is +/// listening on. +struct PortProbe { + /// Command to invoke (`pfiles` on illumos, `ss` on Linux). + command: &'static str, + /// Arguments to pass to `command`. + args: Vec, + /// Substring that must appear on a line for it to belong to the target + /// pid. `None` when the command is already pid-scoped (e.g. `pfiles + /// `). + pid_marker: Option, + /// Pattern that extracts the listening port into capture group 1. + port_re: regex::Regex, +} + +impl PortProbe { + #[cfg(target_os = "illumos")] + fn for_pid(pid: u32) -> Self { + // `pfiles ` lines look like: + // sockname: AF_INET6 ::1 port: 41065 + Self { + command: "pfiles", + args: vec![pid.to_string()], + pid_marker: None, + port_re: regex::Regex::new( + r"sockname:\s+AF_INET6\s+::1?\s+port:\s+(\d+)", + ) + .unwrap(), + } + } + + #[cfg(target_os = "linux")] + fn for_pid(pid: u32) -> Self { + // `ss -tlnpH` lines look like: + // LISTEN 0 128 [::1]:41065 [::]:* users:(("ddmd",pid=12345,fd=8)) + Self { + command: "ss", + args: vec!["-tlnpH".to_string()], + pid_marker: Some(format!("pid={pid}")), + port_re: regex::Regex::new(r"\[::1?\]:(\d+)").unwrap(), + } + } + + #[cfg(target_os = "macos")] + fn for_pid(pid: u32) -> Self { + // `lsof` is part of base macOS, so this compiles and runs without + // any extra install. `lsof -nP -p -iTCP -sTCP:LISTEN` lines + // look like: + // ddmd 12345 user 10u IPv6 0x... 0t0 TCP [::1]:41065 (LISTEN) + Self { + command: "lsof", + args: vec![ + "-nP".to_string(), + "-p".to_string(), + pid.to_string(), + "-iTCP".to_string(), + "-sTCP:LISTEN".to_string(), + ], + pid_marker: None, + port_re: regex::Regex::new(r"\[::1?\]:(\d+)").unwrap(), + } + } + + async fn probe(&self) -> Result, anyhow::Error> { + let output = tokio::process::Command::new(self.command) + .args(&self.args) + .output() + .await + .with_context(|| format!("running {}", self.command))?; + if !output.status.success() { + // The probe command can transiently fail (process exiting, + // permissions, etc.); leave it to the caller to retry. + return Ok(None); + } + let text = std::str::from_utf8(&output.stdout) + .with_context(|| format!("{} output not utf8", self.command))?; + Ok(text + .lines() + .filter(|line| { + self.pid_marker.as_deref().is_none_or(|m| line.contains(m)) + }) + .find_map(|line| { + self.port_re + .captures(line)? + .get(1)? + .as_str() + .parse::() + .ok() + })) + } +} + +/// Ask the kernel which TCP port `pid` is listening on. Retries until either +/// the port is found or [`DDMD_TIMEOUT`] elapses, since `ddmd` may not have +/// finished binding when we first probe. +async fn find_listening_port(pid: u32) -> Result { + let probe = PortProbe::for_pid(pid); + let deadline = Instant::now() + DDMD_TIMEOUT; + loop { + if let Some(port) = probe.probe().await? { + return Ok(port); + } + if Instant::now() >= deadline { + anyhow::bail!( + "kernel reports no listening TCP port for pid {pid} after \ + {DDMD_TIMEOUT:?}" + ); + } + sleep(Duration::from_millis(100)).await; + } +} + async fn discover_port(logfile: String) -> Result { let timeout = Instant::now() + MGD_TIMEOUT; tokio::time::timeout_at(timeout, find_mgd_port_in_log(logfile)) .await - .context("time out while discovering mgd port number")? + .context("time out while discovering port number")? } async fn find_mgd_port_in_log(logfile: String) -> Result { @@ -169,6 +281,126 @@ async fn find_mgd_port_in_log(logfile: String) -> Result { } } +/// Specifies the amount of time we will wait for `ddmd` to bind its admin +/// port, confirmed by asking the kernel which TCP port `ddmd`'s pid is +/// listening on. +const DDMD_TIMEOUT: Duration = Duration::from_secs(5); + +/// Test fixture that spawns and supervises a legit `ddmd` subprocess. +/// +/// Owns a `tokio::process::Child` and a tempdir; discovers the bound admin +/// port by scraping dropshot's startup `local_addr` records; kills the child +/// on `cleanup`/`Drop`. Mirrors `MgdInstance`. +/// +/// `ddmd` runs in sled global zones and switch zones in production. Spawned +/// here with `--api-only`, which serves only the admin API and skips the +/// discovery / exchange / routing daemons that need real network interfaces +/// and illumos-only kernel facilities. Only switch-zone instances +/// are registered in internal DNS as `ServiceName::Ddm`; sled-global-zone +/// instances are accessed locally by their own host (RSS, sled-agent's +/// prefix advertisement, etc.) and don't need DNS publication. +pub struct DdmInstance { + /// Port number the ddmd instance is listening on. + pub port: u16, + /// Arguments provided to the `ddmd` cli command. + pub args: Vec, + /// Child process spawned by running `ddmd`. + pub child: Option, + /// Temporary directory where logging output and other files generated by + /// `ddmd` are stored. + pub data_dir: Option, +} + +impl DdmInstance { + /// Start a `ddmd` instance with `--api-only`, bound to an auto-assigned + /// admin port on localhost. + /// + /// `MgdInstance` discovers its admin port by scraping `local_addr` from + /// `mgd`'s startup logs. That approach does not work for `ddmd`: the + /// dropshot endpoint-registration records that carry `local_addr` are + /// emitted at debug level, and `ddmd`'s slog drain is currently focused + /// on errors, so those lines never reach stdout. + /// + /// Instead, this fixture asks the kernel directly via `pfiles` (illumos), + /// `ss` (Linux), or `lsof` (macOS), which reports the listening TCP port + /// for `ddmd`'s pid regardless of what `ddmd` chose to log. + /// + /// Tracked upstream as oxidecomputer/maghemite#740. Once unified logging + /// levels land, this fixture can revert to the simpler log-scrape pattern + /// that `MgdInstance` uses. + pub async fn start() -> Result { + let temp_dir = TempDir::new()?; + + let args = vec![ + "--admin-addr".to_string(), + "::1".into(), + "--admin-port".into(), + "0".into(), + "--api-only".into(), + "--data-dir".into(), + temp_dir.path().display().to_string(), + ]; + + let child = tokio::process::Command::new("ddmd") + .args(&args) + .stdin(Stdio::null()) + .stdout(Stdio::from(redirect_file(temp_dir.path(), "ddmd_stdout")?)) + .stderr(Stdio::from(redirect_file(temp_dir.path(), "ddmd_stderr")?)) + .spawn() + .with_context(|| { + format!("failed to spawn `ddmd` (with args: {:?})", &args) + })?; + + let pid = + child.id().context("ddmd child has no pid (already exited?)")?; + let temp_dir = temp_dir.keep(); + + let port = find_listening_port(pid).await.with_context(|| { + format!( + "failed to discover ddmd listening port for pid {pid} \ + (see {}/ddmd_stdout, ddmd_stderr)", + temp_dir.display() + ) + })?; + + Ok(Self { port, args, child: Some(child), data_dir: Some(temp_dir) }) + } + + pub async fn cleanup(&mut self) -> Result<(), anyhow::Error> { + if let Some(mut child) = self.child.take() { + child.start_kill().context("Sending SIGKILL to child")?; + child.wait().await.context("waiting for child")?; + } + if let Some(dir) = self.data_dir.take() { + std::fs::remove_dir_all(&dir).with_context(|| { + format!("cleaning up temporary directory {}", dir.display()) + })?; + } + Ok(()) + } +} + +impl Drop for DdmInstance { + fn drop(&mut self) { + if self.child.is_some() || self.data_dir.is_some() { + eprintln!( + "WARN: dropped DdmInstance without cleaning it up first \ + (there may still be a child process running and a \ + temporary directory leaked)" + ); + if let Some(child) = self.child.as_mut() { + let _ = child.start_kill(); + } + if let Some(path) = self.data_dir.take() { + eprintln!( + "WARN: ddmd temporary directory leaked: {}", + path.display() + ); + } + } + } +} + #[cfg(test)] mod tests { use super::find_mgd_port_in_log; @@ -189,6 +421,16 @@ mod tests { .expect("Cannot find 'mgd' on PATH. Refer to README.md for installation instructions"); } + #[tokio::test] + async fn test_ddmd_in_path() { + tokio::process::Command::new("ddmd") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .spawn() + .expect("Cannot find 'ddmd' on PATH. Refer to README.md for installation instructions"); + } + #[tokio::test] async fn test_discover_local_listening_port() { // Write some data to a fake log file diff --git a/tools/dendrite_stub_checksums b/tools/dendrite_stub_checksums index fee34d7b634..249cae438f1 100644 --- a/tools/dendrite_stub_checksums +++ b/tools/dendrite_stub_checksums @@ -1,3 +1,3 @@ -CIDL_SHA256_ILLUMOS="5dab6b11e339db1c82f9e994935eee4b3c22bbc37cf76f5cebd46198b32d8de9" -CIDL_SHA256_LINUX_DPD="1108e680e45b604fb66d70150720afb2be71eb59d6863a5e7ac3eaf13da2143a" -CIDL_SHA256_LINUX_SWADM="dc5c4445f307053be8648e8d88f0ee728d4ce95bb6d9b983c5fdb8b716e5695a" +CIDL_SHA256_ILLUMOS="15eed4d125f1088d9a531d0bf812a8217ba1d9daa21d8ae986a5635a90ef951e" +CIDL_SHA256_LINUX_DPD="33a8ed6930808c865024b72e92b65b5deaad135624e73ccf5d0ac841eb6c5375" +CIDL_SHA256_LINUX_SWADM="f4355a1e93f1625f5405cdbf3724fdea2238bec463fc262043e353c2920872eb" diff --git a/tools/dendrite_version b/tools/dendrite_version index dc314e9897c..ce27ab0a48b 100644 --- a/tools/dendrite_version +++ b/tools/dendrite_version @@ -1 +1 @@ -COMMIT="50ed531201ea5f85115141a2203d551fbbdaa3da" +COMMIT="90efdc6cf618523320bc403f1b9484dbd7f762be" diff --git a/tools/install_builder_prerequisites.sh b/tools/install_builder_prerequisites.sh index 0f1df7d2528..d79a923ca1f 100755 --- a/tools/install_builder_prerequisites.sh +++ b/tools/install_builder_prerequisites.sh @@ -230,6 +230,7 @@ retry xtask download \ console \ dendrite-stub \ maghemite-mgd \ + maghemite-ddmd \ transceiver-control # Validate the PATH: diff --git a/tools/maghemite_ddm_openapi_version b/tools/maghemite_ddm_openapi_version index 060b3a13efb..50676248181 100644 --- a/tools/maghemite_ddm_openapi_version +++ b/tools/maghemite_ddm_openapi_version @@ -1 +1 @@ -COMMIT="7696ee48d5ee29a917dea459e281fe2e8ff20513" +COMMIT="5060d5864df864741dac8a0a4078fa4ecc3c73c8" diff --git a/tools/maghemite_mg_openapi_version b/tools/maghemite_mg_openapi_version index 060b3a13efb..50676248181 100644 --- a/tools/maghemite_mg_openapi_version +++ b/tools/maghemite_mg_openapi_version @@ -1 +1 @@ -COMMIT="7696ee48d5ee29a917dea459e281fe2e8ff20513" +COMMIT="5060d5864df864741dac8a0a4078fa4ecc3c73c8" diff --git a/tools/maghemite_mgd_checksums b/tools/maghemite_mgd_checksums index 470facaa671..f3089a771c8 100644 --- a/tools/maghemite_mgd_checksums +++ b/tools/maghemite_mgd_checksums @@ -1,2 +1,4 @@ -CIDL_SHA256="301d31ca481e4822f69484feacca31dd08a7c4aae87d96641d384bda3178d2f3" -MGD_LINUX_SHA256="95f9759a5fde2784d148c81df2218d29adde1d27fb72d5dbcf534de6450f0f7c" \ No newline at end of file +CIDL_SHA256="96f75128b9c6f836a8c9e55ff06272cfc342f55b3c817bcc9d1e04b0c1f4eb80" +MGD_LINUX_SHA256="62e9cbc035720b90896033ff620a0b9af2ff8ba3da0c49d7e42911ce6941c35a" +MG_DDM_SHA256="7ab1f395b5bbe257a7533642b237706503e1aee6df84832f9e8ae840096be79c" +DDMD_LINUX_SHA256="ad02e88ad5da63f8086bf94f5c216156414031708e52642b1d1a07f43c765f8e" \ No newline at end of file diff --git a/tools/softnpu_version b/tools/softnpu_version index 0fc9248f4f1..95f6d356e81 100644 --- a/tools/softnpu_version +++ b/tools/softnpu_version @@ -1,2 +1,2 @@ -COMMIT="e4c3130538b192988737161f4a4dc97272f02b9c" -SHA2="5776bfcd80b685080c383b19b04b46ba9b985de18f722f1523f456d7ce59d0c5" +COMMIT="284c6830722548714128e63ea04bcca78ee27154" +SHA2="9dc7bc632692d2f895296bc3d2af4b1a6e75c2d2942d7512c5792d5aab6d8411" diff --git a/tools/update_crucible.sh b/tools/update_crucible.sh index 63b0d643cf4..6b59bce3a05 100755 --- a/tools/update_crucible.sh +++ b/tools/update_crucible.sh @@ -18,13 +18,14 @@ function usage { PACKAGES=( "crucible" "crucible-pantry" - "crucible-dtrace" + "crucible-utils" ) CRATES=( "crucible-agent-client" "crucible-pantry-client" "crucible-smf" + "crucible-common" ) REPO="oxidecomputer/crucible" diff --git a/tools/update_maghemite.sh b/tools/update_maghemite.sh index 0051397b51d..0a482cb4440 100755 --- a/tools/update_maghemite.sh +++ b/tools/update_maghemite.sh @@ -54,6 +54,12 @@ function update_mgd { SHA_LINUX=$(get_sha "$REPO" "$TARGET_COMMIT" "mgd" "linux") OUTPUT_LINUX=$(printf "MGD_LINUX_SHA256=\"%s\"\n" "$SHA_LINUX") + SHA_MG_DDM=$(get_sha "$REPO" "$TARGET_COMMIT" "mg-ddm" "image") + OUTPUT_MG_DDM=$(printf "MG_DDM_SHA256=\"%s\"\n" "$SHA_MG_DDM") + + SHA_DDMD_LINUX=$(get_sha "$REPO" "$TARGET_COMMIT" "ddmd" "linux") + OUTPUT_DDMD_LINUX=$(printf "DDMD_LINUX_SHA256=\"%s\"\n" "$SHA_DDMD_LINUX") + if [ -n "$DRY_RUN" ]; then MGD_PATH="/dev/null" else @@ -61,7 +67,7 @@ function update_mgd { fi echo "Updating Maghemite mgd from: $TARGET_COMMIT" set -x - printf "$OUTPUT\n$OUTPUT_LINUX" > $MGD_PATH + printf "$OUTPUT\n$OUTPUT_LINUX\n$OUTPUT_MG_DDM\n$OUTPUT_DDMD_LINUX" > $MGD_PATH set +x } diff --git a/workspace-hack/Cargo.toml b/workspace-hack/Cargo.toml index e04f06954c6..0410cffce72 100644 --- a/workspace-hack/Cargo.toml +++ b/workspace-hack/Cargo.toml @@ -20,6 +20,7 @@ workspace = true [dependencies] ahash = { version = "0.8.12" } aho-corasick = { version = "1.1.4" } +anstream = { version = "0.6.21" } anyhow = { version = "1.0.102", features = ["backtrace"] } base16ct = { version = "0.2.0", default-features = false, features = ["alloc"] } base64 = { version = "0.22.1" } @@ -32,8 +33,8 @@ bytes = { version = "1.11.1", features = ["serde"] } camino = { version = "1.2.2", default-features = false, features = ["serde1"] } chrono = { version = "0.4.44", features = ["serde"] } cipher = { version = "0.4.4", default-features = false, features = ["block-padding", "zeroize"] } -clap = { version = "4.5.60", features = ["cargo", "derive", "env", "wrap_help"] } -clap_builder = { version = "4.5.60", default-features = false, features = ["cargo", "color", "env", "std", "suggestions", "usage", "wrap_help"] } +clap = { version = "4.6.1", features = ["cargo", "derive", "env", "unstable-styles", "wrap_help"] } +clap_builder = { version = "4.6.0", default-features = false, features = ["cargo", "env", "std", "suggestions", "unstable-styles", "usage", "wrap_help"] } const-oid = { version = "0.9.6", default-features = false, features = ["db", "std"] } crossbeam-epoch = { version = "0.9.18" } crossbeam-utils = { version = "0.8.21" } @@ -84,7 +85,7 @@ log = { version = "0.4.29", default-features = false, features = ["std"] } managed = { version = "0.8.0", default-features = false, features = ["alloc", "map"] } memchr = { version = "2.8.0" } newtype-uuid = { version = "1.3.2", features = ["proptest1"] } -nix = { version = "0.31.2", features = ["fs", "net", "signal"] } +nix = { version = "0.31.2", features = ["feature", "fs", "net", "signal", "uio"] } num-bigint-dig = { version = "0.8.6", default-features = false, features = ["i128", "prime", "serde", "u64_digit", "zeroize"] } num-integer = { version = "0.1.46", features = ["i128"] } num-iter = { version = "0.1.45", default-features = false, features = ["i128"] } @@ -111,7 +112,7 @@ rand_chacha-468e82937335b1c9 = { package = "rand_chacha", version = "0.3.1", def regex = { version = "1.12.3" } regex-automata = { version = "0.4.14", default-features = false, features = ["dfa", "hybrid", "meta", "nfa", "perf", "std", "unicode"] } regex-syntax = { version = "0.8.10" } -reqwest-594e8ee84c453af0 = { package = "reqwest", version = "0.13.2", default-features = false, features = ["blocking", "cookies", "http2", "json", "query", "rustls", "stream"] } +reqwest-594e8ee84c453af0 = { package = "reqwest", version = "0.13.2", features = ["blocking", "cookies", "json", "query", "stream"] } reqwest-5ef9efb8ec2df382 = { package = "reqwest", version = "0.12.28", features = ["blocking", "json", "rustls-tls", "stream"] } rsa = { version = "0.9.10", features = ["serde", "sha2"] } rustls = { version = "0.23.37", features = ["ring"] } @@ -145,7 +146,6 @@ tokio-rustls = { version = "0.26.4", default-features = false, features = ["logg tokio-stream = { version = "0.1.18", features = ["net", "sync"] } tokio-util = { version = "0.7.18", features = ["codec", "io-util", "rt", "time"] } toml = { version = "0.7.8" } -toml_datetime-ca01ad9e24f5d932 = { package = "toml_datetime", version = "0.7.5", features = ["serde"] } toml_edit-3c51e837cfc5589a = { package = "toml_edit", version = "0.22.27", features = ["serde"] } toml_parser = { version = "1.0.9" } tough = { version = "0.22.0", default-features = false, features = ["http"] } @@ -165,6 +165,7 @@ zip-3b31131e45eafb45 = { package = "zip", version = "0.6.6", default-features = [build-dependencies] ahash = { version = "0.8.12" } aho-corasick = { version = "1.1.4" } +anstream = { version = "0.6.21" } anyhow = { version = "1.0.102", features = ["backtrace"] } base16ct = { version = "0.2.0", default-features = false, features = ["alloc"] } base64 = { version = "0.22.1" } @@ -178,8 +179,8 @@ camino = { version = "1.2.2", default-features = false, features = ["serde1"] } cc = { version = "1.2.56", default-features = false, features = ["parallel"] } chrono = { version = "0.4.44", features = ["serde"] } cipher = { version = "0.4.4", default-features = false, features = ["block-padding", "zeroize"] } -clap = { version = "4.5.60", features = ["cargo", "derive", "env", "wrap_help"] } -clap_builder = { version = "4.5.60", default-features = false, features = ["cargo", "color", "env", "std", "suggestions", "usage", "wrap_help"] } +clap = { version = "4.6.1", features = ["cargo", "derive", "env", "unstable-styles", "wrap_help"] } +clap_builder = { version = "4.6.0", default-features = false, features = ["cargo", "env", "std", "suggestions", "unstable-styles", "usage", "wrap_help"] } const-oid = { version = "0.9.6", default-features = false, features = ["db", "std"] } crossbeam-epoch = { version = "0.9.18" } crossbeam-utils = { version = "0.8.21" } @@ -231,7 +232,7 @@ log = { version = "0.4.29", default-features = false, features = ["std"] } managed = { version = "0.8.0", default-features = false, features = ["alloc", "map"] } memchr = { version = "2.8.0" } newtype-uuid = { version = "1.3.2", features = ["proptest1"] } -nix = { version = "0.31.2", features = ["fs", "net", "signal"] } +nix = { version = "0.31.2", features = ["feature", "fs", "net", "signal", "uio"] } num-bigint-dig = { version = "0.8.6", default-features = false, features = ["i128", "prime", "serde", "u64_digit", "zeroize"] } num-integer = { version = "0.1.46", features = ["i128"] } num-iter = { version = "0.1.45", default-features = false, features = ["i128"] } @@ -258,7 +259,7 @@ rand_chacha-468e82937335b1c9 = { package = "rand_chacha", version = "0.3.1", def regex = { version = "1.12.3" } regex-automata = { version = "0.4.14", default-features = false, features = ["dfa", "hybrid", "meta", "nfa", "perf", "std", "unicode"] } regex-syntax = { version = "0.8.10" } -reqwest-594e8ee84c453af0 = { package = "reqwest", version = "0.13.2", default-features = false, features = ["blocking", "cookies", "http2", "json", "query", "rustls", "stream"] } +reqwest-594e8ee84c453af0 = { package = "reqwest", version = "0.13.2", features = ["blocking", "cookies", "json", "query", "stream"] } reqwest-5ef9efb8ec2df382 = { package = "reqwest", version = "0.12.28", features = ["blocking", "json", "rustls-tls", "stream"] } rsa = { version = "0.9.10", features = ["serde", "sha2"] } rustls = { version = "0.23.37", features = ["ring"] } @@ -295,7 +296,6 @@ tokio-rustls = { version = "0.26.4", default-features = false, features = ["logg tokio-stream = { version = "0.1.18", features = ["net", "sync"] } tokio-util = { version = "0.7.18", features = ["codec", "io-util", "rt", "time"] } toml = { version = "0.7.8" } -toml_datetime-ca01ad9e24f5d932 = { package = "toml_datetime", version = "0.7.5", features = ["serde"] } toml_edit-3c51e837cfc5589a = { package = "toml_edit", version = "0.22.27", features = ["serde"] } toml_parser = { version = "1.0.9" } tough = { version = "0.22.0", default-features = false, features = ["http"] } @@ -305,8 +305,8 @@ usdt = { version = "0.6.0" } usdt-impl-3b31131e45eafb45 = { package = "usdt-impl", version = "0.6.0", default-features = false, features = ["des"] } usdt-impl-d8f496e17d97b5cb = { package = "usdt-impl", version = "0.5.0", default-features = false, features = ["asm", "des"] } uuid = { version = "1.23.0", features = ["serde", "v4"] } -vergen = { version = "9.0.6", features = ["cargo", "rustc"] } -vergen-lib = { version = "0.1.6", features = ["cargo", "git", "rustc"] } +vergen = { version = "9.1.0", features = ["cargo", "rustc"] } +vergen-lib = { version = "9.1.0", features = ["cargo", "git", "rustc"] } winnow = { version = "0.7.14" } x509-cert = { version = "0.2.5" } zerocopy = { version = "0.8.40", default-features = false, features = ["derive", "simd"] } @@ -404,7 +404,7 @@ object = { version = "0.37.3", default-features = false, features = ["read", "st rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } -toml_datetime-3b31131e45eafb45 = { package = "toml_datetime", version = "0.6.11", default-features = false, features = ["serde"] } +toml_datetime = { version = "0.6.11", default-features = false, features = ["serde"] } toml_edit-cdcf2f9584511fe6 = { package = "toml_edit", version = "0.19.15", features = ["serde"] } [target.x86_64-unknown-illumos.build-dependencies] @@ -421,7 +421,7 @@ object = { version = "0.37.3", default-features = false, features = ["read", "st rustix-d585fab2519d2d1 = { package = "rustix", version = "0.38.44", features = ["fs", "stdio", "system", "termios"] } rustix-dff4ba8e3ae991db = { package = "rustix", version = "1.1.3", features = ["fs", "stdio", "termios"] } tokio-rustls = { version = "0.26.4", default-features = false, features = ["aws-lc-rs"] } -toml_datetime-3b31131e45eafb45 = { package = "toml_datetime", version = "0.6.11", default-features = false, features = ["serde"] } +toml_datetime = { version = "0.6.11", default-features = false, features = ["serde"] } toml_edit-cdcf2f9584511fe6 = { package = "toml_edit", version = "0.19.15", features = ["serde"] } ### END HAKARI SECTION