# rufish
[](https://crates.io/crates/rufish)
[](https://docs.rs/rufish)
[](https://github.com/dalof41014/rufish/blob/main/LICENSE-MIT)
**`rufish`** is an asynchronous **Redfish client library** written in Rust for BMC/server out-of-band management via the DMTF Redfish REST API.
---
## Features
* ✅ Async HTTP/HTTPS client (based on `reqwest` + `tokio`)
* ✅ Session-based authentication (X-Auth-Token) with fallback to Basic Auth
* ✅ **Builder pattern** — inject custom `reqwest::Client`, existing session tokens, or credentials
* ✅ Session persistence — get/set tokens for cross-restart reuse
* ✅ Self-signed certificate support (common for BMCs)
* ✅ Typed Rust structs for all major Redfish resources
* ✅ High-level API for common operations:
* Systems, Chassis, Managers
* Power & Thermal monitoring
* Processors, Memory, Storage, Drives
* Ethernet Interfaces (System & Manager)
* Chassis Indicator LED (LocationIndicatorActive / IndicatorLED)
* Power control (On/Off/Restart/Cycle)
* Boot override (PXE, BIOS Setup, HDD, etc.)
* Account management
* Log services
* Update & Event services
* ✅ **Enriched APIs** for UI/dashboard integration:
* System Health Summary (aggregated status from thermal/power/processors/memory)
* Storage Overview (controllers + RAID volumes + unassigned drives in one call)
* Enriched Log Entries (auto-translated via message registry)
* User Management (create/delete/change password/set role/unlock)
* Simplified Event Subscription
* ✅ **SSE (Server-Sent Events)** — real-time event streaming from BMC
* ✅ **BIOS Config Export/Import** — export from one system, diff, and apply to others
* ✅ **Hardware Topology** — full system/chassis/manager/processor/memory tree for UI
* ✅ **Firmware Update with Progress** — update + poll with progress callback
* ✅ **Auto-Retry + Session Renewal** — automatic re-login on 401, exponential backoff on network errors
* ✅ **Multi-Host Pool** — manage fleets of BMCs with concurrency control
* ✅ Low-level GET/POST/PATCH/DELETE for any Redfish endpoint
* ✅ Registry metadata support — parse BIOS/attribute/message registries into UI-friendly structs
* ✅ Proper error handling with typed errors
---
## Quick Start
```rust
use rufish::RedfishClient;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = RedfishClient::new("10.0.0.5", "admin", "password")?;
client.login().await?;
// Service Root
let root = client.get_service_root().await?;
println!("Redfish version: {:?}", root.redfish_version);
// List systems
let systems = client.list_systems().await?;
println!("Systems: {:?}", systems.members_count);
// Get system details
let sys = client.get_system("1").await?;
println!("Model: {:?}, Power: {:?}", sys.model, sys.power_state);
// Power control
client.power_on("1").await?;
client.graceful_shutdown("1").await?;
// Thermal monitoring
let thermal = client.get_thermal("1").await?;
for t in thermal.temperatures.unwrap_or_default() {
println!("{}: {}°C", t.name.unwrap_or_default(), t.reading_celsius.unwrap_or(0.0));
}
// Boot override
client.set_boot_pxe("1").await?;
client.logout().await?;
Ok(())
}
```
### Builder Pattern
```rust
use rufish::RedfishClient;
// Custom reqwest client (e.g. native-tls, http1_only)
let custom = reqwest::Client::builder()
.use_native_tls()
.http1_only()
.danger_accept_invalid_certs(true)
.build()?;
let client = RedfishClient::builder("10.0.0.5")
.credentials("admin", "password")
.client(custom)
.build()?;
// Or restore a persisted session (no login needed)
let client = RedfishClient::builder("10.0.0.5")
.credentials("admin", "password")
.session("saved-token", "/redfish/v1/SessionService/Sessions/1")
.build()?;
```
---
## API Summary
### Session & Construction
| `new(host, user, pass)` | Create client (HTTPS, accepts self-signed certs) |
| `builder(host)` | Start building a client with custom options |
| `.credentials(user, pass)` | Set credentials (builder) |
| `.client(reqwest_client)` | Inject custom reqwest Client (builder) |
| `.session(token, uri)` | Inject existing session token (builder) |
| `.build()` | Finalize and create the client (builder) |
| `set_session(token, uri)` | Inject session token after construction |
| `session_token()` | Get current token (for persistence) |
| `session_uri()` | Get current session URI |
| `login()` | Establish Redfish session |
| `logout()` | Close session |
### Resources (GET)
| `get_service_root()` | Service Root |
| `list_systems()` / `get_system(id)` | Computer Systems |
| `list_chassis()` / `get_chassis(id)` | Chassis |
| `list_managers()` / `get_manager(id)` | Managers (BMC) |
| `get_power(chassis_id)` | Power readings & supplies |
| `get_thermal(chassis_id)` | Temperatures & fans |
| `list_processors(sys)` / `get_processor(sys, id)` | CPUs |
| `list_memory(sys)` / `get_memory(sys, id)` | DIMMs |
| `list_storage(sys)` / `get_storage(sys, id)` | Storage controllers |
| `list_ethernet_interfaces(sys)` | NICs |
| `list_manager_ethernet_interfaces(mgr)` | BMC network interfaces |
| `get_manager_ethernet_interface(mgr, id)` | Get BMC NIC details |
| `patch_manager_ethernet_interface(mgr, id, body)` | Update BMC NIC settings |
| `get_chassis_indicator(chassis)` | Get Indicator LED state |
| `get_account_service()` / `list_accounts()` | User accounts |
| `get_update_service()` | Firmware update service |
| `get_event_service()` | Event subscriptions |
| `list_log_entries(mgr, log)` | Log entries |
### Actions (POST/PATCH)
| `reset_system(id, type)` | Reset with any ResetType |
| `power_on(id)` | Power on |
| `power_off(id)` | Force power off |
| `graceful_shutdown(id)` | ACPI shutdown |
| `graceful_restart(id)` | Graceful restart |
| `force_restart(id)` | Force restart |
| `power_cycle(id)` | Power cycle |
| `set_boot_override(id, target, enabled)` | Set boot source |
| `set_boot_pxe(id)` | Boot to PXE |
| `set_boot_bios(id)` | Boot to BIOS Setup |
| `set_chassis_indicator(chassis, on)` | Set Indicator LED on/off |
| `reset_manager(id, type)` | Reset BMC |
| `clear_log(mgr, log)` | Clear log service |
### Virtual Media
| `list_virtual_media(mgr)` | List virtual media devices |
| `get_virtual_media(mgr, id)` | Get virtual media status |
| `insert_media(mgr, id, url)` | Mount ISO/IMG from URL |
| `eject_media(mgr, id)` | Unmount media |
### BIOS & Secure Boot
| `get_bios(sys)` | Get current BIOS attributes |
| `get_bios_settings(sys)` | Get pending BIOS settings |
| `set_bios_attributes(sys, attrs)` | Set BIOS attributes (next boot) |
| `reset_bios_defaults(sys)` | Reset BIOS to factory defaults |
| `get_bios_attributes_full(sys)` | Get BIOS attrs with registry metadata (UI-friendly) |
| `get_secure_boot(sys)` | Get Secure Boot status |
| `set_secure_boot(sys, enabled)` | Enable/disable Secure Boot |
### Registry Metadata (for UI)
| `list_registries()` | List all available registries |
| `get_registry(id)` | Get raw registry JSON by ID |
| `get_message_registry(id)` | Get parsed message registry (events/errors) |
| `get_attributes_full(reg_id, path)` | Get any attribute registry merged with current values |
The `get_bios_attributes_full()` method returns `Vec<RegistryAttribute>` with:
- Display names, descriptions, and grouping for form layout
- Attribute types (Enum/String/Integer/Boolean/Password)
- Allowed values with display names (for dropdowns)
- Current and pending values
- Read-only / hidden flags
- Value bounds and string length limits
### Enriched APIs (UI/Dashboard)
| `get_system_health(sys, chassis)` | Aggregated health: CPU, memory, thermal, fans, PSU |
| `get_enriched_logs(mgr, log)` | Log entries with message registry translation |
| `get_storage_overview(sys)` | Controllers + volumes + unassigned drives |
| `list_users()` | All user accounts as simplified structs |
| `create_user(name, pass, role)` | Create a new user |
| `change_password(id, pass)` | Change user password |
| `set_user_role(id, role)` | Change user role |
| `set_user_enabled(id, enabled)` | Enable/disable user |
| `unlock_user(id)` | Unlock a locked account |
| `delete_user(id)` | Delete a user account |
| `subscribe(params)` | Simplified event subscription |
| `subscribe_sse()` | Open SSE stream for real-time events |
| `export_bios_config(sys)` | Export BIOS config as portable struct |
| `import_bios_config(sys, config)` | Apply a BIOS config to a system |
| `diff_bios_config(sys, config)` | Compare config vs current settings |
| `get_topology(sys)` | Full hardware topology tree |
| `update_firmware_with_progress(uri, timeout, cb)` | Firmware update with progress callback |
| `get_with_retry(path, retries)` | GET with auto session renewal + backoff |
| `patch_with_retry(path, body, retries)` | PATCH with auto retry |
| `post_with_retry(path, body, retries)` | POST with auto retry |
### Multi-Host Pool
```rust
use rufish::RedfishPool;
let mut pool = RedfishPool::new(10); // max 10 concurrent
pool.add("bmc-01", "admin", "pass")?;
pool.add("bmc-02", "admin", "pass")?;
pool.login_all().await;
let results = pool.for_each(|client| async move {
client.power_on("1").await
}).await;
```
### Storage / RAID
| `list_volumes(sys, storage)` | List RAID volumes |
| `get_volume(sys, storage, vol)` | Get volume details |
| `create_volume(sys, storage, body)` | Create RAID volume |
| `delete_volume(sys, storage, vol)` | Delete volume |
| `get_drive(path)` | Get drive details |
### Network & Certificates
| `get_network_protocol(mgr)` | BMC network services (NTP, SSH, etc.) |
| `set_network_protocol(mgr, settings)` | Update network settings |
| `list_serial_interfaces(mgr)` | List serial interfaces |
| `list_certificates(mgr)` | List HTTPS certificates |
| `replace_certificate(path, pem, type)` | Replace certificate |
### Event Subscriptions
| `list_subscriptions()` | List event subscriptions |
| `create_subscription(dest, types, ctx)` | Create subscription |
| `delete_subscription(id)` | Delete subscription |
### Firmware Update
| `list_firmware_inventory()` | List firmware items |
| `get_firmware_item(id)` | Get firmware version info |
| `simple_update(image_uri)` | Trigger firmware update from URI |
### Tasks & Pagination
| `list_tasks()` | List async tasks |
| `get_task(id)` | Get task status |
| `wait_task(id, timeout_secs)` | Poll until task completes |
| `get_all_members(path)` | Auto-follow @odata.nextLink |
### Raw Access
| `get(path)` | GET any endpoint (returns `serde_json::Value`) |
| `post(path, body)` | POST action |
| `patch(path, body)` | PATCH resource |
| `delete(path)` | DELETE resource |
---
## Dependencies
```toml
[dependencies]
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
thiserror = "1"
log = "0.4"
```
---
## License
MIT OR Apache-2.0