From 04c592a4e45d23c295624c35eeab494ee6a42e6f Mon Sep 17 00:00:00 2001 From: s3rj1k Date: Fri, 26 Jun 2026 08:43:48 +0000 Subject: [PATCH] feat: per-machine BMC vendor override Pin a Redfish BMC vendor per machine, forced into libredfish instead of auto-detection. The value is a plain string matched against libredfish's RedfishVendor enum at client creation (an unknown name warns and falls back), so NICo keeps no vendor list. It rides on BmcAccessInfo so every client_by_info caller honors it, plus the direct instance-power and force-delete paths. - db: machines.bmc_vendor_override column + migration - api: UpdateMachineBmcVendorOverride RPC, field on Machine - redfish: client_by_info forces the override - cli: machine vendor-override set/clear/show Signed-off-by: s3rj1k --- crates/admin-cli/src/machine/mod.rs | 6 ++ .../src/machine/vendor_override/args.rs | 74 +++++++++++++++++++ .../src/machine/vendor_override/cmd.rs | 62 ++++++++++++++++ .../src/machine/vendor_override/mod.rs | 32 ++++++++ crates/admin-cli/src/rpc.rs | 12 +++ crates/api-core/src/api.rs | 7 ++ crates/api-core/src/handlers/instance.rs | 6 +- crates/api-core/src/handlers/machine.rs | 42 ++++++++++- ...0625120000_machine_bmc_vendor_override.sql | 4 + crates/api-db/src/machine.rs | 17 +++++ crates/api-db/src/machine_interface.rs | 25 +++++-- crates/api-model/src/machine/json.rs | 3 + crates/api-model/src/machine/mod.rs | 6 ++ crates/redfish/src/libredfish/conv.rs | 65 ++++++++++++++++ crates/redfish/src/libredfish/mod.rs | 4 +- crates/rpc/proto/forge.proto | 17 +++++ crates/rpc/src/model/machine/mod.rs | 1 + crates/utils/src/redfish.rs | 3 + .../machine/machine-vendor-override-clear.md | 52 +++++++++++++ .../machine/machine-vendor-override-set.md | 56 ++++++++++++++ .../machine/machine-vendor-override-show.md | 52 +++++++++++++ .../machine/machine-vendor-override.md | 51 +++++++++++++ .../commands/machine/machine.md | 1 + 23 files changed, 588 insertions(+), 10 deletions(-) create mode 100644 crates/admin-cli/src/machine/vendor_override/args.rs create mode 100644 crates/admin-cli/src/machine/vendor_override/cmd.rs create mode 100644 crates/admin-cli/src/machine/vendor_override/mod.rs create mode 100644 crates/api-db/migrations/20260625120000_machine_bmc_vendor_override.sql create mode 100644 docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-clear.md create mode 100644 docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-set.md create mode 100644 docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-show.md create mode 100644 docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override.md diff --git a/crates/admin-cli/src/machine/mod.rs b/crates/admin-cli/src/machine/mod.rs index fcb019562f..cb0cfdb82f 100644 --- a/crates/admin-cli/src/machine/mod.rs +++ b/crates/admin-cli/src/machine/mod.rs @@ -27,6 +27,7 @@ pub mod nvlink_info; pub mod positions; pub mod reboot; pub mod show; +pub mod vendor_override; #[cfg(test)] mod tests; @@ -89,4 +90,9 @@ pub enum Cmd { Positions(positions::Args), #[clap(subcommand, about = "Update/show NVLink info for an MNNVL machine")] NvlinkInfo(nvlink_info::Args), + #[clap( + subcommand, + about = "Pin or clear the Redfish BMC vendor override for a machine" + )] + VendorOverride(vendor_override::Args), } diff --git a/crates/admin-cli/src/machine/vendor_override/args.rs b/crates/admin-cli/src/machine/vendor_override/args.rs new file mode 100644 index 0000000000..3f1d63d6dd --- /dev/null +++ b/crates/admin-cli/src/machine/vendor_override/args.rs @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use carbide_uuid::machine::MachineId; +use clap::Parser; + +#[derive(Parser, Debug, Clone)] +pub enum Args { + #[clap(about = "Pin the Redfish BMC vendor for a machine")] + Set(VendorOverrideSet), + #[clap(about = "Clear the Redfish BMC vendor override for a machine")] + Clear(VendorOverrideClear), + #[clap(about = "Show the Redfish BMC vendor override for a machine")] + Show(VendorOverrideShow), +} + +#[derive(Parser, Debug, Clone)] +#[command(after_long_help = "\ +EXAMPLES: + +Force a machine's BMC vendor to Dell: + $ nico-admin-cli machine vendor-override set 12345678-1234-5678-90ab-cdef01234567 \ + --vendor Dell + +")] +pub struct VendorOverrideSet { + #[clap(help = "The machine whose BMC vendor should be pinned")] + pub machine: MachineId, + #[clap( + long, + help = "RedfishVendor to force (e.g. Dell, Supermicro, NvidiaDpu, Hpe, Lenovo)" + )] + pub vendor: String, +} + +#[derive(Parser, Debug, Clone)] +#[command(after_long_help = "\ +EXAMPLES: + +Clear a machine's BMC vendor override (return to automatic detection): + $ nico-admin-cli machine vendor-override clear 12345678-1234-5678-90ab-cdef01234567 + +")] +pub struct VendorOverrideClear { + #[clap(help = "The machine whose BMC vendor override should be cleared")] + pub machine: MachineId, +} + +#[derive(Parser, Debug, Clone)] +#[command(after_long_help = "\ +EXAMPLES: + +Show a machine's pinned BMC vendor (or that none is set): + $ nico-admin-cli machine vendor-override show 12345678-1234-5678-90ab-cdef01234567 + +")] +pub struct VendorOverrideShow { + #[clap(help = "The machine whose BMC vendor override should be shown")] + pub machine: MachineId, +} diff --git a/crates/admin-cli/src/machine/vendor_override/cmd.rs b/crates/admin-cli/src/machine/vendor_override/cmd.rs new file mode 100644 index 0000000000..27390dfa6c --- /dev/null +++ b/crates/admin-cli/src/machine/vendor_override/cmd.rs @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +use carbide_uuid::machine::MachineId; +use rpc::Machine; + +use super::args::{Args, VendorOverrideClear, VendorOverrideSet, VendorOverrideShow}; +use crate::errors::{CarbideCliError, CarbideCliResult}; +use crate::rpc::ApiClient; + +pub async fn vendor_override(api_client: &ApiClient, cmd: Args) -> CarbideCliResult<()> { + match cmd { + Args::Set(cmd) => set(api_client, cmd).await, + Args::Clear(cmd) => clear(api_client, cmd).await, + Args::Show(cmd) => show(api_client, cmd).await, + } +} + +async fn fetch_machine(api_client: &ApiClient, machine_id: MachineId) -> CarbideCliResult { + let mut machines = api_client + .get_machines_by_ids(&[machine_id]) + .await? + .machines; + machines.pop().ok_or_else(|| { + CarbideCliError::GenericError(format!("Machine with ID {machine_id} was not found")) + }) +} + +async fn set(api_client: &ApiClient, cmd: VendorOverrideSet) -> CarbideCliResult<()> { + api_client + .update_machine_bmc_vendor_override(cmd.machine, Some(cmd.vendor)) + .await +} + +async fn clear(api_client: &ApiClient, cmd: VendorOverrideClear) -> CarbideCliResult<()> { + api_client + .update_machine_bmc_vendor_override(cmd.machine, None) + .await +} + +async fn show(api_client: &ApiClient, cmd: VendorOverrideShow) -> CarbideCliResult<()> { + let machine = fetch_machine(api_client, cmd.machine).await?; + match machine.bmc_vendor_override.as_deref() { + Some(vendor) => println!("{vendor}"), + None => println!("not set (automatic detection)"), + } + Ok(()) +} diff --git a/crates/admin-cli/src/machine/vendor_override/mod.rs b/crates/admin-cli/src/machine/vendor_override/mod.rs new file mode 100644 index 0000000000..1fd27fe848 --- /dev/null +++ b/crates/admin-cli/src/machine/vendor_override/mod.rs @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +pub mod args; +pub mod cmd; + +pub use args::Args; + +use crate::cfg::run::Run; +use crate::cfg::runtime::RuntimeContext; +use crate::errors::CarbideCliResult; + +impl Run for Args { + async fn run(self, ctx: &mut RuntimeContext) -> CarbideCliResult<()> { + cmd::vendor_override(&ctx.api_client, self).await?; + Ok(()) + } +} diff --git a/crates/admin-cli/src/rpc.rs b/crates/admin-cli/src/rpc.rs index a82db3a0a3..ac9bb7134f 100644 --- a/crates/admin-cli/src/rpc.rs +++ b/crates/admin-cli/src/rpc.rs @@ -2045,6 +2045,18 @@ impl ApiClient { Ok(self.0.update_machine_metadata(request).await?) } + pub async fn update_machine_bmc_vendor_override( + &self, + machine_id: MachineId, + bmc_vendor_override: Option, + ) -> CarbideCliResult<()> { + let request = ::rpc::forge::MachineBmcVendorOverrideUpdateRequest { + machine_id: Some(machine_id), + bmc_vendor_override, + }; + Ok(self.0.update_machine_bmc_vendor_override(request).await?) + } + pub async fn update_rack_metadata( &self, rack_id: RackId, diff --git a/crates/api-core/src/api.rs b/crates/api-core/src/api.rs index ae9f19380d..2709122d6f 100644 --- a/crates/api-core/src/api.rs +++ b/crates/api-core/src/api.rs @@ -1170,6 +1170,13 @@ impl Forge for Api { crate::handlers::machine::update_machine_metadata(self, request).await } + async fn update_machine_bmc_vendor_override( + &self, + request: Request, + ) -> std::result::Result, Status> { + crate::handlers::machine::update_machine_bmc_vendor_override(self, request).await + } + async fn update_rack_metadata( &self, request: Request, diff --git a/crates/api-core/src/handlers/instance.rs b/crates/api-core/src/handlers/instance.rs index 650a74d79e..28434dd23f 100644 --- a/crates/api-core/src/handlers/instance.rs +++ b/crates/api-core/src/handlers/instance.rs @@ -1041,6 +1041,10 @@ pub(crate) async fn invoke_power( // but instead queue it for the state handler. That will avoid racing // with other internal reboot requests from the state handler. let bmc_ip = bmc_ip.to_string(); + let vendor_override = carbide_redfish::libredfish::conv::redfish_vendor_override( + &bmc_ip, + snapshot.host_snapshot.bmc_vendor_override.as_deref(), + ); let client = api .redfish_pool .create_client( @@ -1049,7 +1053,7 @@ pub(crate) async fn invoke_power( RedfishAuth::Key(CredentialKey::BmcCredentials { credential_type: BmcCredentialType::BmcRoot { bmc_mac_address }, }), - None, + vendor_override, ) .await .map_err(|e| CarbideError::internal(e.to_string()))?; diff --git a/crates/api-core/src/handlers/machine.rs b/crates/api-core/src/handlers/machine.rs index 403a7f80fb..c136ed9514 100644 --- a/crates/api-core/src/handlers/machine.rs +++ b/crates/api-core/src/handlers/machine.rs @@ -302,6 +302,41 @@ pub(crate) async fn update_machine_metadata( Ok(tonic::Response::new(())) } +pub(crate) async fn update_machine_bmc_vendor_override( + api: &Api, + request: Request, +) -> std::result::Result, tonic::Status> { + log_request_data(&request); + let request = request.into_inner(); + let machine_id = convert_and_log_machine_id(request.machine_id.as_ref())?; + + let mut txn = api.txn_begin().await?; + if db::machine::find_one(&mut txn, &machine_id, MachineSearchConfig::default()) + .await? + .is_none() + { + return Err(CarbideError::NotFoundError { + kind: "machine", + id: machine_id.to_string(), + } + .into()); + } + + // Store the override as a plain string. An empty or absent value clears it. + // libredfish does the vendor matching when the client is built, so the API + // keeps no vendor list of its own. + let bmc_vendor_override = match request.bmc_vendor_override { + Some(name) if !name.is_empty() => Some(name), + _ => None, + }; + + db::machine::update_bmc_vendor_override(&mut txn, &machine_id, bmc_vendor_override).await?; + + txn.commit().await?; + + Ok(tonic::Response::new(())) +} + pub(crate) async fn admin_force_delete_machine( api: &Api, request: Request, @@ -472,6 +507,11 @@ pub(crate) async fn admin_force_delete_machine( "BMC IP and MAC address for machine was found. Trying to perform Bios unlock", ); + let vendor_override = carbide_redfish::libredfish::conv::redfish_vendor_override( + &ip_address, + machine.bmc_vendor_override.as_deref(), + ); + match api .redfish_pool .create_client( @@ -480,7 +520,7 @@ pub(crate) async fn admin_force_delete_machine( RedfishAuth::Key(CredentialKey::BmcCredentials { credential_type: BmcCredentialType::BmcRoot { bmc_mac_address }, }), - None, + vendor_override, ) .await { diff --git a/crates/api-db/migrations/20260625120000_machine_bmc_vendor_override.sql b/crates/api-db/migrations/20260625120000_machine_bmc_vendor_override.sql new file mode 100644 index 0000000000..06a0c02156 --- /dev/null +++ b/crates/api-db/migrations/20260625120000_machine_bmc_vendor_override.sql @@ -0,0 +1,4 @@ +-- Add bmc_vendor_override to machines so an operator can pin the Redfish BMC +-- vendor for a machine. NULL means automatic detection. The value is a +-- RedfishVendor variant name passed down into libredfish as the forced vendor. +ALTER TABLE machines ADD COLUMN bmc_vendor_override text; diff --git a/crates/api-db/src/machine.rs b/crates/api-db/src/machine.rs index a5bfa05f18..fe4bcbcd54 100644 --- a/crates/api-db/src/machine.rs +++ b/crates/api-db/src/machine.rs @@ -899,6 +899,23 @@ pub async fn update_metadata( } } +/// Set or clear the operator pinned Redfish BMC vendor override for a machine. +/// Passing None clears it. +pub async fn update_bmc_vendor_override( + txn: &mut PgConnection, + machine_id: &MachineId, + bmc_vendor_override: Option, +) -> Result<(), DatabaseError> { + let query = "UPDATE machines SET bmc_vendor_override = $1 WHERE id = $2"; + sqlx::query(query) + .bind(bmc_vendor_override) + .bind(machine_id) + .execute(txn) + .await + .map_err(|e| DatabaseError::query(query, e))?; + Ok(()) +} + /// Only does the update if the passed observation is newer than any existing one pub async fn update_network_status_observation( txn: &mut PgConnection, diff --git a/crates/api-db/src/machine_interface.rs b/crates/api-db/src/machine_interface.rs index 4612c37049..ad88956b7a 100644 --- a/crates/api-db/src/machine_interface.rs +++ b/crates/api-db/src/machine_interface.rs @@ -373,17 +373,28 @@ pub async fn lookup_bmc_access_info( ip: IpAddr, port: Option, ) -> DatabaseResult { - let mac_address = find_by_ip(db, ip) - .await? - .ok_or_else(|| DatabaseError::NotFoundError { - kind: "Machine Interface", - id: ip.to_string(), - })? - .mac_address; + // Resolve the BMC interface MAC and the owning machine's vendor override in + // one query so every client_by_info caller can act on the override. + let query = r"SELECT mi.mac_address, m.bmc_vendor_override + FROM machine_interface_addresses mia + INNER JOIN machine_interfaces mi ON mi.id = mia.interface_id + LEFT JOIN machines m ON m.id = mi.machine_id + WHERE mia.address = $1::inet + LIMIT 1"; + let row: Option<(MacAddress, Option)> = sqlx::query_as(query) + .bind(ip) + .fetch_optional(db) + .await + .map_err(|e| DatabaseError::query(query, e))?; + let (mac_address, bmc_vendor_override) = row.ok_or_else(|| DatabaseError::NotFoundError { + kind: "Machine Interface", + id: ip.to_string(), + })?; Ok(BmcAccessInfo { host: ip.to_string(), port, mac_address, + bmc_vendor_override, }) } diff --git a/crates/api-model/src/machine/json.rs b/crates/api-model/src/machine/json.rs index 56bf193210..561f515911 100644 --- a/crates/api-model/src/machine/json.rs +++ b/crates/api-model/src/machine/json.rs @@ -91,6 +91,8 @@ pub struct MachineSnapshotPgJson { pub interfaces: Vec, pub topology: Vec, pub bmc_info: BmcInfo, + #[serde(default)] + pub bmc_vendor_override: Option, pub labels: HashMap, pub name: String, pub description: String, @@ -181,6 +183,7 @@ impl TryFrom for Machine { interfaces: value.interfaces, hardware_info, bmc_info: value.bmc_info, + bmc_vendor_override: value.bmc_vendor_override, last_reboot_time: value.last_reboot_time, last_cleanup_time: value.last_cleanup_time, last_discovery_time: value.last_discovery_time, diff --git a/crates/api-model/src/machine/mod.rs b/crates/api-model/src/machine/mod.rs index f176f1ed27..3b42a31a51 100644 --- a/crates/api-model/src/machine/mod.rs +++ b/crates/api-model/src/machine/mod.rs @@ -781,6 +781,12 @@ pub struct Machine { /// The BMC info for this machine pub bmc_info: BmcInfo, + /// Operator pinned Redfish BMC vendor for this machine. When set, it is + /// passed to libredfish as the forced vendor instead of detection from the + /// service root. None means automatic detection. Holds a RedfishVendor + /// variant name. + pub bmc_vendor_override: Option, + /// Last time when machine came up. pub last_reboot_time: Option>, diff --git a/crates/redfish/src/libredfish/conv.rs b/crates/redfish/src/libredfish/conv.rs index 2705d7de86..45b14f862a 100644 --- a/crates/redfish/src/libredfish/conv.rs +++ b/crates/redfish/src/libredfish/conv.rs @@ -167,6 +167,37 @@ pub fn bmc_vendor(r: libredfish::model::service_root::RedfishVendor) -> BMCVendo } } +/// Parse a Redfish vendor name into the libredfish RedfishVendor. The set of +/// valid names is owned by libredfish through its derived Deserialize over the +/// enum variants, so NICo keeps no vendor list of its own. Returns None when the +/// name matches no variant. +pub fn redfish_vendor_from_str( + name: &str, +) -> Option { + use serde::Deserialize; + let de = serde::de::value::StrDeserializer::::new(name); + libredfish::model::service_root::RedfishVendor::deserialize(de).ok() +} + +/// Resolve a stored BMC vendor override string into a forced RedfishVendor. None +/// or empty means no override. A name that matches no vendor logs a warning and +/// returns None so the caller uses automatic detection. host only labels the log. +pub fn redfish_vendor_override( + host: &str, + raw: Option<&str>, +) -> Option { + let name = raw.filter(|s| !s.is_empty())?; + let vendor = redfish_vendor_from_str(name); + if vendor.is_none() { + tracing::warn!( + bmc = %host, + bmc_vendor_override = %name, + "bmc_vendor_override matches no known Redfish vendor, using automatic detection" + ); + } + vendor +} + impl IntoModel for libredfish::model::component_integrity::CaCertificate { fn into_model(self) -> CaCertificate { CaCertificate { @@ -192,3 +223,37 @@ impl IntoModel for libredfish::model::component_integrity::Evidence { } } } + +#[cfg(test)] +mod vendor_override_tests { + use libredfish::model::service_root::RedfishVendor; + + use super::{redfish_vendor_from_str, redfish_vendor_override}; + + #[test] + fn parses_known_variant_names() { + assert_eq!(redfish_vendor_from_str("Dell"), Some(RedfishVendor::Dell)); + assert_eq!( + redfish_vendor_from_str("NvidiaDpu"), + Some(RedfishVendor::NvidiaDpu) + ); + } + + #[test] + fn rejects_unknown_or_miscased_names() { + assert_eq!(redfish_vendor_from_str("dell"), None); + assert_eq!(redfish_vendor_from_str("NotARealVendor"), None); + assert_eq!(redfish_vendor_from_str(""), None); + } + + #[test] + fn override_helper_handles_empty_and_unmatched() { + assert_eq!(redfish_vendor_override("bmc", None), None); + assert_eq!(redfish_vendor_override("bmc", Some("")), None); + assert_eq!( + redfish_vendor_override("bmc", Some("Dell")), + Some(RedfishVendor::Dell) + ); + assert_eq!(redfish_vendor_override("bmc", Some("bogus")), None); + } +} diff --git a/crates/redfish/src/libredfish/mod.rs b/crates/redfish/src/libredfish/mod.rs index de666ce60b..d5f3a5c9bb 100644 --- a/crates/redfish/src/libredfish/mod.rs +++ b/crates/redfish/src/libredfish/mod.rs @@ -97,11 +97,13 @@ pub trait RedfishClientPool: Send + Sync + 'static { &self, access: &BmcAccessInfo, ) -> Result, RedfishClientCreationError> { + let vendor = + conv::redfish_vendor_override(&access.host, access.bmc_vendor_override.as_deref()); self.create_client( &access.host, access.port, RedfishAuth::for_bmc_mac(access.mac_address), - None, + vendor, ) .await } diff --git a/crates/rpc/proto/forge.proto b/crates/rpc/proto/forge.proto index c6c414dedd..8ec7dd75ea 100644 --- a/crates/rpc/proto/forge.proto +++ b/crates/rpc/proto/forge.proto @@ -340,6 +340,9 @@ service Forge { // Update the Metadata of a Machine rpc UpdateMachineMetadata(MachineMetadataUpdateRequest) returns (google.protobuf.Empty); + // Pin or clear the Redfish BMC vendor override of a Machine + rpc UpdateMachineBmcVendorOverride(MachineBmcVendorOverrideUpdateRequest) returns (google.protobuf.Empty); + // Update the Metadata of a Rack rpc UpdateRackMetadata(RackMetadataUpdateRequest) returns (google.protobuf.Empty); @@ -3609,6 +3612,11 @@ message Machine { optional string last_scout_observed_version = 48; optional DpfMachineState dpf = 49; + + // Operator pinned Redfish BMC vendor for this machine. When set, it is passed + // to libredfish as the forced vendor instead of detection from the service + // root. Empty means automatic detection. Holds a RedfishVendor variant name. + optional string bmc_vendor_override = 50; } message DpfMachineState { @@ -3651,6 +3659,15 @@ message MachineMetadataUpdateRequest { Metadata metadata = 3; } +// Pins or clears the Redfish BMC vendor override stored on a Machine +message MachineBmcVendorOverrideUpdateRequest { + // The ID of the Machine for which the override will apply + common.MachineId machine_id = 1; + + // RedfishVendor variant name to force. Empty or unset clears the override. + optional string bmc_vendor_override = 2; +} + message RackMetadataUpdateRequest { // The ID of the Rack for which the Metadata update will apply common.RackId rack_id = 1; diff --git a/crates/rpc/src/model/machine/mod.rs b/crates/rpc/src/model/machine/mod.rs index b535072746..7ad73212c0 100644 --- a/crates/rpc/src/model/machine/mod.rs +++ b/crates/rpc/src/model/machine/mod.rs @@ -346,6 +346,7 @@ impl From for rpc::forge::Machine { tray_index: machine.tray_index, }), last_scout_observed_version: machine.last_scout_observed_version, + bmc_vendor_override: machine.bmc_vendor_override, dpf, } } diff --git a/crates/utils/src/redfish.rs b/crates/utils/src/redfish.rs index 589a9a239f..a32c06203b 100644 --- a/crates/utils/src/redfish.rs +++ b/crates/utils/src/redfish.rs @@ -25,4 +25,7 @@ pub struct BmcAccessInfo { pub host: String, pub port: Option, pub mac_address: MacAddress, + /// Operator pinned Redfish vendor for this BMC, a RedfishVendor variant + /// name. None means automatic detection. + pub bmc_vendor_override: Option, } diff --git a/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-clear.md b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-clear.md new file mode 100644 index 0000000000..28e10cce13 --- /dev/null +++ b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-clear.md @@ -0,0 +1,52 @@ +# `nico-admin-cli machine vendor-override clear` + +_[Hardware commands](../../hardware.md) › [machine](./machine.md) › [vendor-override](./machine-vendor-override.md) › **clear**_ + +## NAME + +nico-admin-cli-machine-vendor-override-clear - Clear the Redfish BMC +vendor override for a machine + +## SYNOPSIS + +**nico-admin-cli machine vendor-override clear** \[**--extended**\] +\[**--sort-by**\] \[**-h**\|**--help**\] \<*MACHINE*\> + +## DESCRIPTION + +Clear the Redfish BMC vendor override for a machine + +## OPTIONS + +**--extended** +Extended result output. + +This used by measured boot, where basic output contains just what you +probably care about, and "extended" output also dumps out all the +internal UUIDs that are used to associate instances. + +**--sort-by** *\* \[default: primary-id\] +Sort output by specified field\ + +\ +*Possible values:* + +- primary-id: Sort by the primary id + +- state: Sort by state + +**-h**, **--help** +Print help (see a summary with -h) + +\<*MACHINE*\> +The machine whose BMC vendor override should be cleared + +## Examples + +```sh +nico-admin-cli machine vendor-override clear 12345678-1234-5678-90ab-cdef01234567 +``` + +--- + +**See also:** [Hardware commands](../../hardware.md) · [CLI reference index](../../README.md) diff --git a/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-set.md b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-set.md new file mode 100644 index 0000000000..6aea159b81 --- /dev/null +++ b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-set.md @@ -0,0 +1,56 @@ +# `nico-admin-cli machine vendor-override set` + +_[Hardware commands](../../hardware.md) › [machine](./machine.md) › [vendor-override](./machine-vendor-override.md) › **set**_ + +## NAME + +nico-admin-cli-machine-vendor-override-set - Pin the Redfish BMC vendor +for a machine + +## SYNOPSIS + +**nico-admin-cli machine vendor-override set** \<**--vendor**\> +\[**--extended**\] \[**--sort-by**\] \[**-h**\|**--help**\] +\<*MACHINE*\> + +## DESCRIPTION + +Pin the Redfish BMC vendor for a machine + +## OPTIONS + +**--vendor** *\* +RedfishVendor to force (e.g. Dell, Supermicro, NvidiaDpu, Hpe, Lenovo) + +**--extended** +Extended result output. + +This used by measured boot, where basic output contains just what you +probably care about, and "extended" output also dumps out all the +internal UUIDs that are used to associate instances. + +**--sort-by** *\* \[default: primary-id\] +Sort output by specified field\ + +\ +*Possible values:* + +- primary-id: Sort by the primary id + +- state: Sort by state + +**-h**, **--help** +Print help (see a summary with -h) + +\<*MACHINE*\> +The machine whose BMC vendor should be pinned + +## Examples + +```sh +nico-admin-cli machine vendor-override set 12345678-1234-5678-90ab-cdef01234567 --vendor Dell +``` + +--- + +**See also:** [Hardware commands](../../hardware.md) · [CLI reference index](../../README.md) diff --git a/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-show.md b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-show.md new file mode 100644 index 0000000000..38071e4a71 --- /dev/null +++ b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override-show.md @@ -0,0 +1,52 @@ +# `nico-admin-cli machine vendor-override show` + +_[Hardware commands](../../hardware.md) › [machine](./machine.md) › [vendor-override](./machine-vendor-override.md) › **show**_ + +## NAME + +nico-admin-cli-machine-vendor-override-show - Show the Redfish BMC +vendor override for a machine + +## SYNOPSIS + +**nico-admin-cli machine vendor-override show** \[**--extended**\] +\[**--sort-by**\] \[**-h**\|**--help**\] \<*MACHINE*\> + +## DESCRIPTION + +Show the Redfish BMC vendor override for a machine + +## OPTIONS + +**--extended** +Extended result output. + +This used by measured boot, where basic output contains just what you +probably care about, and "extended" output also dumps out all the +internal UUIDs that are used to associate instances. + +**--sort-by** *\* \[default: primary-id\] +Sort output by specified field\ + +\ +*Possible values:* + +- primary-id: Sort by the primary id + +- state: Sort by state + +**-h**, **--help** +Print help (see a summary with -h) + +\<*MACHINE*\> +The machine whose BMC vendor override should be shown + +## Examples + +```sh +nico-admin-cli machine vendor-override show 12345678-1234-5678-90ab-cdef01234567 +``` + +--- + +**See also:** [Hardware commands](../../hardware.md) · [CLI reference index](../../README.md) diff --git a/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override.md b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override.md new file mode 100644 index 0000000000..6cefb0aff8 --- /dev/null +++ b/docs/manuals/nico-admin-cli/commands/machine/machine-vendor-override.md @@ -0,0 +1,51 @@ +# `nico-admin-cli machine vendor-override` + +_[Hardware commands](../../hardware.md) › [machine](./machine.md) › **vendor-override**_ + +## NAME + +nico-admin-cli-machine-vendor-override - Pin or clear the Redfish BMC +vendor override for a machine + +## SYNOPSIS + +**nico-admin-cli machine vendor-override** \[**--extended**\] +\[**--sort-by**\] \[**-h**\|**--help**\] \<*subcommands*\> + +## DESCRIPTION + +Pin or clear the Redfish BMC vendor override for a machine + +## OPTIONS + +**--extended** +Extended result output. + +This used by measured boot, where basic output contains just what you +probably care about, and "extended" output also dumps out all the +internal UUIDs that are used to associate instances. + +**--sort-by** *\* \[default: primary-id\] +Sort output by specified field\ + +\ +*Possible values:* + +- primary-id: Sort by the primary id + +- state: Sort by state + +**-h**, **--help** +Print help (see a summary with -h) + +## Subcommands + +| Subcommand | Description | +|---|---| +| [`set`](./machine-vendor-override-set.md) | Pin the Redfish BMC vendor for a machine | +| [`clear`](./machine-vendor-override-clear.md) | Clear the Redfish BMC vendor override for a machine | +| [`show`](./machine-vendor-override-show.md) | Show the Redfish BMC vendor override for a machine | + +--- + +**See also:** [Hardware commands](../../hardware.md) · [CLI reference index](../../README.md) diff --git a/docs/manuals/nico-admin-cli/commands/machine/machine.md b/docs/manuals/nico-admin-cli/commands/machine/machine.md index 909fadb898..0dd6a7e4d6 100644 --- a/docs/manuals/nico-admin-cli/commands/machine/machine.md +++ b/docs/manuals/nico-admin-cli/commands/machine/machine.md @@ -51,6 +51,7 @@ Print help (see a summary with -h) | [`hardware-info`](./machine-hardware-info.md) | Update/show machine hardware info | | [`positions`](./machine-positions.md) | Show physical location info for machines in rack-based systems | | [`nvlink-info`](./machine-nvlink-info.md) | Update/show NVLink info for an MNNVL machine | +| [`vendor-override`](./machine-vendor-override.md) | Pin or clear the Redfish BMC vendor override for a machine | ---