Skip to content

feat: Add rune-scripted libredfish vendor#91

Open
s3rj1k wants to merge 1 commit into
NVIDIA:mainfrom
s3rj1k:feat/rune
Open

feat: Add rune-scripted libredfish vendor#91
s3rj1k wants to merge 1 commit into
NVIDIA:mainfrom
s3rj1k:feat/rune

Conversation

@s3rj1k

@s3rj1k s3rj1k commented Jun 19, 2026

Copy link
Copy Markdown

Add script-programmable BMC vendor with ability to do local-file based vendor overrides per BMC address.

This enables adopting NICo to weird/buggy Redfish OEM implementations in-runtime instead of constantly doing NICo recompilation on each encountered error.

Additionally it allows hot-fixing specific machines that already have a proper Vendor implemented in library, say for example old Super-micro nodes that do not expose all required by NICo endpoints.

Signed-off-by: s3rj1k <evasive.gyron@gmail.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 19, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@s3rj1k

s3rj1k commented Jun 19, 2026

Copy link
Copy Markdown
Author

Example rune script that overrides some of BMC broken endpoints

// Rune vendor override for the server (host) BMC: methods defined here replace the standard
// Redfish implementation, everything else falls back. Selected via a vendor-override entry
// { "vendor": "Rune", "script": "<path>/generic.rn" }. `ctx` exposes get/patch/post/delete
// (JSON) plus system_id()/manager_id()/variant()/bmc_address(), and helpers like sha256().
// Starting point, extend per the server BMC.

// PATCH the system `Boot` object; `mode`/`http_boot_uri` omitted when absent.
pub async fn set_boot_override(ctx, target, enabled, mode, http_boot_uri) {
    let boot = #{
        "BootSourceOverrideTarget": target,
        "BootSourceOverrideEnabled": enabled,
    };
    if let Some(m) = mode {
        boot["BootSourceOverrideMode"] = m;
    }
    if let Some(uri) = http_boot_uri {
        boot["HttpBootUri"] = uri;
    }
    ctx.patch(`Systems/${ctx.system_id()}`, #{ "Boot": boot }).await?;
    None
}

pub async fn boot_once(ctx, target) {
    set_boot_override(ctx, target, "Once", None, None).await
}

pub async fn boot_first(ctx, target) {
    set_boot_override(ctx, target, "Continuous", None, None).await
}

// limited BMC, boot the PXE network device first (persistent)
pub async fn set_boot_order_dpu_first(ctx) {
    set_boot_override(ctx, "Pxe", "Continuous", None, None).await
}

// limited BMC cannot apply BIOS profiles or run setup jobs, so machine_setup is a no-op and
// is_*_setup report complete to let host bring-up proceed. is_ipmi_over_lan_enabled returns
// true so NICo skips the unsupported NetworkProtocol PATCH (IPMI is configured out-of-band)
pub async fn machine_setup(ctx) {
    None
}

pub async fn is_bios_setup(ctx) {
    true
}

pub async fn is_boot_order_setup(ctx) {
    true
}

pub async fn is_ipmi_over_lan_enabled(ctx) {
    true
}

// limited BMC 404s the standard FirmwareInventory; return empty so fetch_service() succeeds.
pub async fn get_software_inventories(ctx) {
    []
}

// limited BMC exposes only a vendor-specific UpdateService; return a default so callers skip it.
pub async fn get_update_service(ctx) {
    #{}
}

// limited BMC 404s Systems/{id}/SecureBoot; report it disabled so site-explorer records "off".
// The libredfish SecureBoot model requires @odata.id, @odata.type, Id, Name (rest optional).
pub async fn get_secure_boot(ctx) {
    #{
        "@odata.id": "/redfish/v1/Systems/Self/SecureBoot",
        "@odata.type": "#SecureBoot.v1_1_0.SecureBoot",
        "Id": "SecureBoot",
        "Name": "UEFI Secure Boot",
        "SecureBootEnable": false,
        "SecureBootCurrentBoot": "Disabled",
    }
}

// Blank Chassis SerialNumber (empty/missing) can't match an ExpectedMachine, so when blank after
// trim, synthesize a stable sha256(bmc_address + manager_id). Real serials pass through.
pub async fn get_chassis(ctx, id) {
    let resp = ctx.get(`Chassis/${id}`).await?;
    let chassis = resp["body"];
    let blank = match chassis.get("SerialNumber") {
        Some(sn) => if sn is String { sn.trim().is_empty() } else { true },
        None => true,
    };
    if blank {
        chassis["SerialNumber"] = sha256(`${ctx.bmc_address()}${ctx.manager_id()}`);
    }
    chassis
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant