Skip to content

feat(redfish): add UpdateService upload APIs#97

Open
jayzhudev wants to merge 2 commits into
NVIDIA:mainfrom
jayzhudev:feat/multipart-upload
Open

feat(redfish): add UpdateService upload APIs#97
jayzhudev wants to merge 2 commits into
NVIDIA:mainfrom
jayzhudev:feat/multipart-upload

Conversation

@jayzhudev
Copy link
Copy Markdown

@jayzhudev jayzhudev commented May 16, 2026

Description

Adds stream-based Redfish UpdateService multipart upload support.

  • Extends the transport-agnostic Bmc trait with multipart_update.
  • Extends bmc-http HttpClient with post_multipart_update.
  • Implements reqwest multipart POST with UpdateParameters JSON and a named UpdateFile stream.
  • Uses the BMC-provided MultipartHttpPushUri from the UpdateService resource.
  • Supports generated MultipartUpdateParameters and arbitrary serializable UpdateParameters payloads, including OEM fields.
  • Adds mock BMC support so product code can test multipart uploads without an HTTP server.

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Related Issues (Optional)

Breaking Changes

  • This PR contains breaking changes

Adds required methods to Bmc and HttpClient. Implementations must implement multipart update support.

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Additional Notes

Multipart upload is reader/stream-based so callers can provide file-backed streams or other data sources.

Signed-off-by: Jay Zhu jayzhu@nvidia.com

@jayzhudev jayzhudev requested review from poroh and yoks May 16, 2026 05:37
@jayzhudev jayzhudev force-pushed the feat/multipart-upload branch 2 times, most recently from 5334ae9 to 518e8b8 Compare May 16, 2026 05:53
@jayzhudev jayzhudev self-assigned this May 16, 2026
Comment thread redfish/src/update_service/mod.rs Outdated
Comment on lines +70 to +76
/// Common Redfish UpdateService multipart upload URI.
#[cfg(feature = "bmc-http")]
pub const UPDATE_MULTIPART_URI: &str = "/redfish/v1/UpdateService/update-multipart";

/// Common Redfish UpdateService raw file upload URI.
#[cfg(feature = "bmc-http")]
pub const UPDATE_URI: &str = "/redfish/v1/UpdateService/update";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different platforms may define these URIs differently. Redfish provides properties with value of these properties:

UPDATE_MULTIPART_URI value must be taken from MultipartHttpPushUri property (See:
https://docs.rs/nv-redfish/latest/nv_redfish/schema/update_service/struct.UpdateService.html#structfield.multipart_http_push_uri )

UPDATE_URI value from HttpPushUri (https://docs.rs/nv-redfish/latest/nv_redfish/schema/update_service/struct.UpdateService.html#structfield.http_push_uri)

These values can be accessed using data property of UpdateService struct.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching this. Agreed, using UpdateService's data properties now.

Comment thread redfish/src/update_service/mod.rs Outdated
Comment on lines +78 to +99
#[cfg(feature = "bmc-http")]
const FORCE_UPDATE_PARAMETER: &str = "ForceUpdate";
#[cfg(feature = "bmc-http")]
const TARGETS_PARAMETER: &str = "Targets";

/// Typed `UpdateParameters` part for Redfish UpdateService multipart uploads.
#[cfg(feature = "bmc-http")]
#[derive(Debug, Clone, serde::Serialize, PartialEq, Eq)]
pub struct MultipartUpdateParameters {
/// Force the update even when the service would otherwise reject it by policy.
#[serde(rename = "ForceUpdate")]
pub force_update: bool,
/// Redfish target resource URIs to update.
#[serde(rename = "Targets")]
pub targets: Vec<String>,
/// Additional Redfish or vendor-specific update parameters.
///
/// Use [`Self::with_parameter`] to add entries without colliding with typed
/// `ForceUpdate` or `Targets` fields.
#[serde(flatten)]
additional_parameters: serde_json::Map<String, JsonValue>,
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this is UpdateParameters complex type that is defined by schema here:
https://github.com/DMTF/Redfish-Publications/blob/26c949654e3e510800b05fa8d39cf336f10fa687/csdl/UpdateService_v1.xml#L1016

In nv-redfish we avoid manually implement data structures that are defined by standard. However, CSDL compiler today doesn't have way to bring this to you because it is defined as ComplexType and this type is not attached to the Redfish tree. We had similar approach to this for EntityType (root_patterns). I guess that we should extend it to complex types as well. I'll do it as separate PR.

After my PR of extending root_patterns you can avoid defining these parameters.

Copy link
Copy Markdown
Contributor

@poroh poroh May 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#98

Generated type:

    #[derive(Deserialize, Debug)]
    pub struct UpdateParameters {
        /// An array of URIs that indicate where to apply the update image.
        ///
        /// This property shall contain zero or more URIs that indicate where to apply the update image when
        /// using the URI specified by the `MultipartHttpPushUri` property to push a software image.  These
        /// targets should correspond to software inventory instances or their related items.  If this property
        /// is not present or contains no targets, the service shall apply the software image to all applicable
        /// targets, as determined by the service.  If the target specifies a device resource, the software
        /// image file shall be applied to the specified device.  If the target specifies a resource
        /// collection, the software image shall be applied to each applicable member of the specified
        /// collection.  If the target resource specifies an `Aggregate` resource, the software image file
        /// shall be applied to each applicable element of the specified aggregate.  If the target resource
        /// specifies a `ComputerSystem` resource, the software image file shall be applied to the applicable
        /// components within the specified computer system.
        #[serde(rename = "Targets", default, deserialize_with = "de_optional_nullable")]
        pub targets: Option<Option<Vec<redfish::edm::String>>>,
        /// The OEM extension property.
        ///
        /// This property shall contain the OEM extensions.  All values for properties contained in this object
        /// shall conform to the Redfish Specification-described requirements.
        #[serde(rename = "Oem", default)]
        pub oem: Option<redfish::resource::Oem>,
        /// An indication of whether the service should bypass update policies when applying the provided
        /// image.  The default is `false`.
        ///
        /// This property shall indicate whether the service should bypass update policies when applying the
        /// provided image, such as allowing a component to be downgraded.  Services may contain update
        /// policies that are never bypassed, such as minimum version enforcement.  If the client does not
        /// provide this parameter, the service shall default this value to `false`.
        #[serde(rename = "ForceUpdate", default)]
        pub force_update: Option<redfish::edm::Boolean>,
        /// An indication of whether the service stages the image on target devices for a client to activate at
        /// a later time.
        ///
        /// This property shall indicate whether the service stages the image on target devices for a client to
        /// activate at a later time with the `Activate` action on the `SoftwareInventory` resource.  If the
        /// client does not provide this parameter, the service shall default this value to `false`.
        #[serde(rename = "Stage", default)]
        pub stage: Option<redfish::edm::Boolean>,
        /// An indication of whether the service adds the image to the local image store.
        ///
        /// This property shall indicate whether the service adds the image to the resource collection
        /// referenced by the `LocalImageStore` property.  If the client does not provide this parameter, the
        /// service shall default this value to `false`.
        #[serde(rename = "LocalImage", default)]
        pub local_image: Option<redfish::edm::Boolean>,
        /// An array of URIs that indicate where not to apply the update image.
        ///
        /// This property shall contain zero or more URIs that indicate where not to apply the update image.
        /// This property shall be ignored if the `Targets` property is provided and contains at least one
        /// member.  These excluded targets should correspond to software inventory instances or their related
        /// items.  If this parameter is not present or contains no targets, the service shall apply the
        /// software image to all applicable targets, as determined by the service.  If an excluded target
        /// specifies a device resource, the software image file shall not be applied to that specified device.
        ///  If an excluded target specifies a resource collection, the software image shall not be applied to
        /// each applicable member of the specified collection.  If an excluded target resource specifies an
        /// `Aggregate` resource, the software image file shall not be applied to each applicable element of
        /// the specified aggregate.  If an excluded target resource specifies a `ComputerSystem` resource, the
        /// software image file shall not be applied to the applicable components within the specified computer
        /// system.
        #[serde(rename = "ExcludeTargets", default)]
        pub exclude_targets: Option<Vec<redfish::edm::String>>,
    }

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll use the build-time generated code after your PR as reference to reduce manual definitions of structs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've rebased this PR on #98 and used the generated struct. This PR should only be merged after #98 is merged.

Comment thread redfish/src/update_service/mod.rs Outdated
Comment on lines +375 to +376
#[cfg(feature = "bmc-http")]
impl UpdateService<HttpBmc<ReqwestClient>> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nv-redfish consistently implements everything through BMC trait. This approach opens possibility to write testable code without need to bring up mock HTTP server. So, we need to keep it this way.

I don't know full upload service business so far but if we need to extend BMC trait to accommodate requirements we need to do so.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment! Multipart update is used for firmware uploads through Redfish. We can:

  • extend the Bmc trait -> breaking change, or
  • use Bmc trait as a supertrait for this -> testable without HTTP server and no breaking change

Which way would suite this crate better?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a trait UpdateServiceUploadBmc to extend Bmc supertrait. Code is testable without a mock HTTP server.

@jayzhudev jayzhudev force-pushed the feat/multipart-upload branch 3 times, most recently from 660cf86 to fd45b7a Compare May 16, 2026 18:49
@jayzhudev jayzhudev requested a review from poroh May 16, 2026 18:57
## Description

Adds stream-based Redfish UpdateService multipart upload support.

- Extends the transport-agnostic `Bmc` trait with `multipart_update`.
- Extends `bmc-http` `HttpClient` with `post_multipart_update`.
- Implements reqwest multipart POST with `UpdateParameters` JSON and a named `UpdateFile` stream.
- Uses the BMC-provided `MultipartHttpPushUri` from the UpdateService resource.
- Supports generated `MultipartUpdateParameters` and arbitrary serializable UpdateParameters payloads, including OEM fields.
- Adds mock BMC support so product code can test multipart uploads without an HTTP server.

## Type of Change

- [x] **Add** - New feature or capability
- [ ] **Change** - Changes in existing functionality
- [ ] **Fix** - Bug fixes
- [ ] **Remove** - Removed features or deprecated functionality
- [ ] **Internal** - Internal changes (refactoring, tests, docs, etc.)

## Related Issues (Optional)

## Breaking Changes

- [x] This PR contains breaking changes

Adds required methods to `Bmc` and `HttpClient`. Implementations must implement multipart update support.

## Testing

- [x] Unit tests added/updated
- [x] Integration tests added/updated
- [ ] Manual testing performed
- [ ] No testing required (docs, internal refactor, etc.)

## Additional Notes

Multipart upload is reader/stream-based so callers can provide file-backed streams or other data sources.

Signed-off-by: Jay Zhu <jayzhu@nvidia.com>
@jayzhudev jayzhudev force-pushed the feat/multipart-upload branch from fd45b7a to f5f95b3 Compare May 20, 2026 03:02
@copy-pr-bot
Copy link
Copy Markdown

copy-pr-bot Bot commented May 20, 2026

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.

Comment thread core/src/bmc.rs
impl<T> UploadReader for T where T: AsyncRead + Send + 'static {}

/// Named data stream accepted by upload methods.
pub struct DataStream<R> {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm not sure if this is right place for it, maybe different file? bmc.rs holds just trait and adding structs here specific only for one of the functions looks out of place

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it too. Let me try to find a better place for it.

Comment thread core/src/bmc.rs
) -> impl Future<Output = Result<ModificationResponse<R>, Self::Error>> + Send;

/// POST a Redfish `UpdateService` multipart upload using a named stream.
fn multipart_update<U, V, R>(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please make sure this signature does conforms Redfish spec about multipart https://www.dmtf.org/sites/default/files/standards/documents/DSP0266_1.23.0.html

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than the upload_timeout, we are not supporting the OEM parts that the spec allows yet. Otherwise it conforms.

OEM specific parts Content-Disposition form-data; name="OemXXXX"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, i would add them here, as optional.

Comment thread README.md Outdated
See `examples/session-token` for Redfish SessionService authentication using
`X-Auth-Token`.

## Update Uploads
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe name it Multipart Uploads? Looks closer to spec, also not sure this part of Readme is where it should ist. But i have no strong opinion here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, could be a left-over from my previous version, removing the whole section from README.

Comment thread bmc-http/src/reqwest.rs

let update_parameters_part = update_parameters_part(update_parameters)?;
let form = Form::new()
.part("UpdateParameters", update_parameters_part)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to support optional parts of form, like Oem data

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, will address this together with signature conformation to the spec, we will likely need the OEM parts in use cases.

Signed-off-by: Jay Zhu <jayzhu@nvidia.com>
Comment thread bmc-http/src/lib.rs
///
/// Accepts a named stream so callers can upload firmware from any source
/// and still send Redfish's required `UpdateFile` multipart part.
fn post_multipart_update<U, V, T>(
Copy link
Copy Markdown
Contributor

@yoks yoks May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels bloated in terms of amount of params, maybe we can reuse post (wrap body), or at least move multipart related data into separate struct.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I used DataStream to wrap some but it does still look borderline too many. Let me think about it.

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.

3 participants