Skip to content

Commit b22549d

Browse files
committed
feat: count firmware update outcomes across every target
Firmware updates run for three targets -- host BIOS/BMC, DPU NICs, and SuperNICs -- and none of their outcomes are countable today: starts, completions, and failures live only in log lines, including the "wrong version after attempted update" case that means an update silently didn't take. This makes all of it countable, with each event replacing its site's log line at the historical level (info for progress, warn for failures) and the same information as structured fields. Two counters from `carbide-instrument`: - `carbide_firmware_updates_total{target, phase}` -- started and completed, per target. For hosts both phases fire, so the started-to-completed gap is the in-flight backlog; the DPU-NIC and SuperNIC targets emit started only, with completion visible in their own state flows. - `carbide_firmware_update_failures_total{target, cause}` -- `no_update_match` (a DPU left ready between snapshot and trigger) and `wrong_version_after_update` (the update ran and the firmware version still doesn't match -- the silent-failure case). Notable details: - Started counts only committed triggers: the DPU-NIC emit fires after `txn.commit()`, so an update whose trigger rolled back is a `no_update_match` failure, never a started update, and the started-minus-completed math stays truthful. - Host completion emits once per machine as its update markers clear, replacing one Vec-debug log line with per-machine lines. - A SuperNIC re-counts one started per scout pass while a device stays mismatched -- each pass issues a real apply command, and the sustained rate is the stuck-device signal. Tests added! This supports #3175 Signed-off-by: Chet Nichols III <chetn@nvidia.com>
1 parent c5ede1e commit b22549d

6 files changed

Lines changed: 315 additions & 35 deletions

File tree

crates/api-core/src/handlers/svpc.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ use rpc::protos::mlx_device::MlxDeviceInfo;
3434
use tonic::{Request, Response, Status};
3535

3636
use crate::api::{Api, log_request_data};
37+
use crate::machine_update_manager::metrics::{
38+
FirmwareUpdatePhase, FirmwareUpdateProgress, FirmwareUpdateTarget,
39+
};
3740
use crate::{CarbideError, CarbideResult};
3841

3942
// Code to handle SVPC specific information.
@@ -197,12 +200,16 @@ fn build_apply_firmware_command<'a>(
197200
return None;
198201
}
199202

200-
tracing::info!(
201-
%machine_id, %pci_name, %part_number, %psid,
202-
observed_fw_version = ?device_info.fw_version_current,
203-
expected_fw_version = %fw_profile.firmware_spec.version,
204-
"firmware version mismatch, applying firmware"
205-
);
203+
carbide_instrument::emit(FirmwareUpdateProgress {
204+
target: FirmwareUpdateTarget::SuperNic,
205+
phase: FirmwareUpdatePhase::Started,
206+
machine_id,
207+
detail: format!(
208+
"pci_name={pci_name} part_number={part_number} psid={psid} \
209+
observed_fw_version={:?} expected_fw_version={}",
210+
device_info.fw_version_current, fw_profile.firmware_spec.version
211+
),
212+
});
206213
Some(Cow::Borrowed(fw_profile))
207214
})();
208215

crates/api-core/src/machine_update_manager/dpu_nic_firmware.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ use sqlx::PgConnection;
2929

3030
use super::dpu_nic_firmware_metrics::DpuNicFirmwareUpdateMetrics;
3131
use super::machine_update_module::MachineUpdateModule;
32+
use super::metrics::{
33+
FirmwareUpdateFailed, FirmwareUpdateFailureCause, FirmwareUpdatePhase, FirmwareUpdateProgress,
34+
FirmwareUpdateTarget,
35+
};
3236
use crate::cfg::file::CarbideConfig;
3337
use crate::machine_update_manager::MachineUpdateManager;
3438
use crate::{CarbideResult, DatabaseError};
@@ -105,11 +109,6 @@ impl MachineUpdateModule for DpuNicFirmwareUpdate {
105109
output + format!("{} ({}) ", dpu.dpu_machine_id, dpu.firmware_version).as_str()
106110
});
107111

108-
tracing::info!(
109-
"Starting DPU updates for host {}: {}",
110-
host_machine_id,
111-
dpu_update_string
112-
);
113112
// If the reprovisioning failed to update the database for a
114113
// given {dpu,host}_machine_id, log it as a warning and don't
115114
// add it to updates_started.
@@ -124,11 +123,13 @@ impl MachineUpdateModule for DpuNicFirmwareUpdate {
124123
{
125124
match reprovisioning_err {
126125
DatabaseError::NotFoundError { id, .. } => {
127-
tracing::warn!(
128-
"failed to trigger reprovisioning for managed host : {} - no update match for id: {}",
129-
host_machine_id,
130-
id
131-
);
126+
carbide_instrument::emit(FirmwareUpdateFailed {
127+
target: FirmwareUpdateTarget::DpuNic,
128+
cause: FirmwareUpdateFailureCause::NoUpdateMatch,
129+
machine_id: host_machine_id,
130+
unmatched_dpu_machine_id: id,
131+
firmware_version: String::new(),
132+
});
132133
continue;
133134
}
134135
_ => {
@@ -139,6 +140,15 @@ impl MachineUpdateModule for DpuNicFirmwareUpdate {
139140

140141
txn.commit().await?;
141142

143+
// Counted only once the trigger is committed: a DPU that left
144+
// ready between snapshot and trigger is a NoUpdateMatch failure,
145+
// not a started update.
146+
carbide_instrument::emit(FirmwareUpdateProgress {
147+
target: FirmwareUpdateTarget::DpuNic,
148+
phase: FirmwareUpdatePhase::Started,
149+
machine_id: host_machine_id,
150+
detail: dpu_update_string,
151+
});
142152
updates_started.insert(host_machine_id);
143153
}
144154

@@ -165,11 +175,13 @@ impl MachineUpdateModule for DpuNicFirmwareUpdate {
165175
);
166176
}
167177
} else {
168-
tracing::warn!(
169-
machine_id = %updated_machine.dpu_machine_id,
170-
firmware_version = %updated_machine.firmware_version,
171-
"Incorrect firmware version after attempted update"
172-
);
178+
carbide_instrument::emit(FirmwareUpdateFailed {
179+
target: FirmwareUpdateTarget::DpuNic,
180+
cause: FirmwareUpdateFailureCause::WrongVersionAfterUpdate,
181+
machine_id: updated_machine.dpu_machine_id,
182+
unmatched_dpu_machine_id: String::new(),
183+
firmware_version: updated_machine.firmware_version,
184+
});
173185
}
174186
}
175187
Ok(())

crates/api-core/src/machine_update_manager/host_firmware.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use sqlx::PgConnection;
3232
use tokio::sync::Mutex;
3333

3434
use super::machine_update_module::MachineUpdateModule;
35+
use super::metrics::{FirmwareUpdatePhase, FirmwareUpdateProgress, FirmwareUpdateTarget};
3536
use crate::CarbideResult;
3637
use crate::cfg::file::CarbideConfig;
3738

@@ -90,15 +91,22 @@ impl MachineUpdateModule for HostFirmwareUpdate {
9091
continue;
9192
}
9293

93-
tracing::info!("Moving {} to host reprovision", machine_update);
94-
9594
db::host_machine_update::trigger_host_reprovisioning_request(
9695
&mut txn,
9796
"Automated",
9897
machine_update,
9998
)
10099
.await?;
101100

101+
// Counted after the trigger succeeds; the commit below spans the
102+
// whole batch, so a later DB error can re-count machines on the
103+
// next pass -- the same repeat-on-retry the old log line had.
104+
carbide_instrument::emit(FirmwareUpdateProgress {
105+
target: FirmwareUpdateTarget::Host,
106+
phase: FirmwareUpdatePhase::Started,
107+
machine_id: *machine_update,
108+
detail: String::new(),
109+
});
102110
updates_started.insert(*machine_update);
103111
}
104112

@@ -109,18 +117,21 @@ impl MachineUpdateModule for HostFirmwareUpdate {
109117
async fn clear_completed_updates(&self, txn: &mut PgConnection) -> CarbideResult<()> {
110118
let completed = db::host_machine_update::find_completed_updates(txn).await?;
111119

112-
if !completed.is_empty() {
113-
tracing::info!("Completed host firmware updates: {completed:?}");
114-
for machine in completed {
115-
db::machine::remove_health_report(
116-
txn,
117-
&machine,
118-
health_report::HealthReportApplyMode::Merge,
119-
HOST_FW_UPDATE_HEALTH_REPORT_SOURCE,
120-
)
121-
.await?;
122-
db::machine::update_update_complete(&machine, true, txn).await?;
123-
}
120+
for machine in completed {
121+
db::machine::remove_health_report(
122+
txn,
123+
&machine,
124+
health_report::HealthReportApplyMode::Merge,
125+
HOST_FW_UPDATE_HEALTH_REPORT_SOURCE,
126+
)
127+
.await?;
128+
db::machine::update_update_complete(&machine, true, txn).await?;
129+
carbide_instrument::emit(FirmwareUpdateProgress {
130+
target: FirmwareUpdateTarget::Host,
131+
phase: FirmwareUpdatePhase::Completed,
132+
machine_id: machine,
133+
detail: String::new(),
134+
});
124135
}
125136
Ok(())
126137
}

0 commit comments

Comments
 (0)