rufish 0.4.0

An asynchronous Redfish client library for BMC/server management in Rust.
Documentation
# rufish

[![Crates.io](https://img.shields.io/crates/v/rufish.svg)](https://crates.io/crates/rufish)
[![Docs.rs](https://docs.rs/rufish/badge.svg)](https://docs.rs/rufish)
[![License](https://img.shields.io/crates/l/rufish.svg)](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

| Method | Description |
|--------|-------------|
| `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)

| Method | Description |
|--------|-------------|
| `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)

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `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)

| Method | Description |
|--------|-------------|
| `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)

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `list_subscriptions()` | List event subscriptions |
| `create_subscription(dest, types, ctx)` | Create subscription |
| `delete_subscription(id)` | Delete subscription |

### Firmware Update

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `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

| Method | Description |
|--------|-------------|
| `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