Some checks failed
Run Check Script / check (pull_request) Failing after 19s
148 lines
4.6 KiB
Rust
148 lines
4.6 KiB
Rust
//! Example: trigger OPNsense firmware upgrade and monitor progress.
|
|
//!
|
|
//! ```text
|
|
//! cargo run --example firmware_upgrade
|
|
//! ```
|
|
//!
|
|
//! Calls `POST /api/core/firmware/update` to trigger a major update,
|
|
//! then polls `GET /api/core/firmware/upgradestatus` for progress.
|
|
|
|
mod common;
|
|
|
|
use serde::Deserialize;
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct UpdateResponse {
|
|
#[serde(default)]
|
|
status: String,
|
|
#[serde(default)]
|
|
msg_uuid: String,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct UpgradeStatus {
|
|
#[serde(default)]
|
|
status: String,
|
|
#[serde(default)]
|
|
log: String,
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let client = common::client_from_env();
|
|
|
|
// Step 1: Check for updates
|
|
println!("Checking for firmware updates...");
|
|
let check: serde_json::Value = client
|
|
.get_typed("core", "firmware", "status")
|
|
.await
|
|
.expect("status call failed");
|
|
|
|
let status = check
|
|
.get("status")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("unknown");
|
|
let status_msg = check
|
|
.get("status_msg")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("");
|
|
println!(" status: {status}");
|
|
println!(" message: {status_msg}");
|
|
|
|
if let Some(upgrade) = check.get("upgrade_packages") {
|
|
println!(
|
|
" upgrade packages: {}",
|
|
serde_json::to_string_pretty(upgrade).unwrap()
|
|
);
|
|
}
|
|
|
|
if status == "none" {
|
|
println!("\nNo status available. Running firmware check first...");
|
|
let check_resp: UpdateResponse = client
|
|
.post_typed("core", "firmware", "check", None::<&()>)
|
|
.await
|
|
.expect("check call failed");
|
|
println!(" check triggered: {check_resp:?}");
|
|
|
|
// Wait for check to complete
|
|
for _ in 0..30 {
|
|
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
|
|
let status: UpgradeStatus = client
|
|
.get_typed("core", "firmware", "upgradestatus")
|
|
.await
|
|
.expect("upgradestatus failed");
|
|
let last_line = status.log.lines().last().unwrap_or("");
|
|
println!(" check status={}, last_log={}", status.status, last_line);
|
|
if status.status == "done" {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Re-check status
|
|
let check2: serde_json::Value = client
|
|
.get_typed("core", "firmware", "status")
|
|
.await
|
|
.expect("status call failed");
|
|
let status2 = check2
|
|
.get("status")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("unknown");
|
|
let msg2 = check2
|
|
.get("status_msg")
|
|
.and_then(|v| v.as_str())
|
|
.unwrap_or("");
|
|
println!("\n Updated status: {status2}");
|
|
println!(" Updated message: {msg2}");
|
|
|
|
if status2 == "update" {
|
|
println!("\nUpdates available. Triggering firmware update...");
|
|
} else {
|
|
println!("\nNo updates available (status={status2}). Exiting.");
|
|
return;
|
|
}
|
|
} else if status != "update" {
|
|
println!("\nFirmware status is '{status}', not 'update'. Exiting.");
|
|
return;
|
|
} else {
|
|
println!("\nUpdates available. Triggering firmware update...");
|
|
}
|
|
|
|
// Step 2: Trigger the update
|
|
let update: UpdateResponse = client
|
|
.post_typed("core", "firmware", "update", None::<&()>)
|
|
.await
|
|
.expect("update call failed");
|
|
println!(" update triggered: {update:?}");
|
|
|
|
// Step 3: Poll for completion
|
|
println!("\nMonitoring upgrade progress...");
|
|
for i in 0..300 {
|
|
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
|
|
|
let status = client
|
|
.get_typed::<UpgradeStatus>("core", "firmware", "upgradestatus")
|
|
.await;
|
|
|
|
match status {
|
|
Ok(s) => {
|
|
let last_lines: Vec<&str> = s.log.lines().rev().take(3).collect();
|
|
println!(
|
|
"[{i:3}] status={}, log tail: {}",
|
|
s.status,
|
|
last_lines.into_iter().rev().collect::<Vec<_>>().join(" | ")
|
|
);
|
|
if s.status == "done" || s.status == "reboot" {
|
|
println!("\nFirmware upgrade complete! Status: {}", s.status);
|
|
if s.status == "reboot" {
|
|
println!("A reboot is required to complete the upgrade.");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
Err(e) => {
|
|
println!("[{i:3}] Connection error (firewall may be rebooting): {e}");
|
|
}
|
|
}
|
|
}
|
|
}
|