RFC: Redfish ETag Support for Boot Order Updates in Smart Proxy and redfish_client

RFC: Redfish ETag Support for Boot Order Updates in Smart Proxy and redfish_client

Context and Problem Statement

What is the problem being solved?

When using the Redfish provider in Foreman’s Smart Proxy to change a host’s boot
device to PXE (e.g. via hammer host boot --device pxe), the operation reports
success, but the actual BMC boot order is not updated.

The root cause appears to be the lack of ETag / If-Match support in the Redfish
path. Some Redfish implementations require ETag-based conditional PATCH operations,
as defined in the Redfish Specification DSP0266 (v1.23.0, Section 6.5, p.34).

Reference:

How does this affect users or developers?

  • Users cannot reliably switch hosts to PXE boot via Foreman.
  • Developers cannot implement correct Redfish PATCH behavior because:
    • redfish_client lacks ETag retrieval support.
    • redfish_client lacks PATCH/PUT with custom headers such as If-Match.
  • Smart Proxy cannot correctly update boot settings without extending the gem.

Background

  • Smart Proxy relies on the external Ruby gem redfish_client.
  • Manual curl with ETag and If-Match succeeds in updating boot settings.
  • Smart Proxy’s generic Redfish path does not handle ETags.

Technical Objectives

  • Implement ETag-aware PATCH support aligned with Redfish standards.
  • Improve reliability of Redfish boot order updates.
  • Follow the Foreman architecture in which Smart Proxy depends on redfish_client.

Proposal

Implement a two-layer solution consistent with the Foreman architecture:

1. Extend redfish_client

  • Add API to retrieve ETag from Redfish resources (e.g., Systems).
  • Add API to allow PATCH/PUT with custom headers, including If-Match.
  • Maintain backward compatibility.

2. Update Smart Proxy Redfish provider (bmc/redfish.rb)

  • Perform a GET request to retrieve the resource’s ETag before updating Boot settings.
  • Use the extended redfish_client API to PATCH with:
    • BootSourceOverrideTarget
    • BootSourceOverrideEnabled
    • If-Match: <etag>
  • Add fallback behavior for resources that do not return ETags.

Technical and Project-Level Impacts

  • Requires coordinated updates to:
    • Smart Proxy
    • redfish_client gem
  • Ensures standards-compliant Redfish behavior.
  • Improves compatibility across diverse BMC implementations.

Impacts

Positive Impacts

  • Reliable Redfish-based boot order updates.
  • Proper implementation of Redfish specification (ETag).
  • Increased compatibility with more BMC models.

Limitations / Considerations

  • Requires changes across two components.
    (Smart Proxy / redfish_client )

Questions / Confirmation

Before proceeding with implementation, I would like to confirm:

**Is it acceptable to proceed with the two-layer approach:

  1. extending redfish_client, and
  2. updating Smart Proxy to use the new ETag-aware API?**

This confirmation will help ensure that the patches align with Foreman’s architectural expectations.


Thank you in advance for your feedback and guidance. I appreciate your time and support.

2 Likes

I don’t have the full context for how Redfish ETags work, but if redfish_client is missing support for them, it seems appropriate to me that changes will be needed to both the redfish_client and smart proxy projects. redfish_client would have the support to pass on the ETag data, which smart proxy would provide. I’m making some assumptions here, of course.

@lstejska or @ekohl might be interested in the details. Have we heard other requests about this missing ETag support yet?

1 Like

Thank you for your feedback.

Regarding the question about whether there have been other requests for missing ETag support:

At the moment, I am not aware of any Foreman community reports explicitly mentioning the lack of ETag / If-Match handling in the Redfish provider.
However, I have confirmed that multiple BMC implementations require ETag-based conditional PATCH operations for boot order updates.

Specifically:

  • Fujitsu iRMC
  • Gigabyte R272-P31 (BMC)

Both require an ETag in order to successfully update the Boot settings via Redfish.
Since this behavior follows the Redfish specification, I believe similar issues may also occur with other vendors.

Thank you.

1 Like

As always @spesnova717 I really appreciate your thoroughness in providing context.

When I read the reference spec it states:

ETags enable clients to conditionally retrieve or update a resource. Resources should include an @odata.etag
property. For a resource, the value shall be the ETag

And later in the etags section:

To reduce unnecessary RESTful accesses to resources, the Redfish service should support the association of a
separate entity tag (ETag) with each resource.

The document doesn’t clearly state if they follow RFC 2119 for the definition of should but the rest of the text all implies it’s optional, not mandatory. However, in practice we know many Redfish implementations don’t exactly follow the spec and we need to be practical because at the end of the day the users want a working system.

I’ve worked with the maintainer before. In Use this Redfish client without sessions · Issue #7 · xlab-steampunk/redfish-client-ruby · GitHub they state they don’t take feature requests, but when I submitted Add a use_session parameter to force basic auth usage by ekohl · Pull Request #8 · xlab-steampunk/redfish-client-ruby · GitHub it was merged and released in a reasonable timeframe.

Since etags are mentioned in the spec it does make sense for the library to include it.

I’m struggling to understand the semantics. Quoting the spec which you’ve helpfully provided:

If a client performs a PUT operation or PATCH operation to update a resource, it should include an ETag from a
previous GET in the HTTP If-Match or If-None-Match header. Both strong and weak ETags are allowed in these
headers. If a service supports the return of the ETag header on a resource, it may respond with the HTTP 428
Precondition Required status code if the If-Match or If-None-Match header is missing from the PUT or PATCH
request for the same resource, as specified in RFC6585. Services should perform weak ETag comparison when
verifying the ETag provided by the client in PUT or PATCH operations. Clients should treat ETags received from
services as opaque values and not modify them when providing them in PUT or PATCH operations.

First of all, this does describe very clearly the scenario you’re describing: it may respond with HTTP 428 Precondition Required.

I never considered this before, but I like this concept for PUT because it can detect something was modified in flight (which is quite common), but for PATCH where you only provide the parameters you explicitly want to set then isn’t it redundant?

Your approach of first doing a GET to get the ETag introduces a race condition where something modifies it in between so I’m thinking about ways to minimize this.

Would it be a reasonable idea to explicitly catch HTTP 428 errors and then fall back to your suggested behavior?

Another thought: does it work to specify If-None-Match to make it unconditional? If-None-Match header - HTTP | MDN suggests * is a special value but perhaps you can also specify a value that is never returned so never matches. This is getting into the hacky territory.

This is how we’ve made changes in the past. fixes #37486 - Request BMC using Redfish without reaching sessions limit by neomilium · Pull Request #895 · theforeman/smart-proxy · GitHub is the Smart Proxy part of the PR I mentioned at the start.

So the important part is to properly declare the dependency, in this case in bundler.d/bmc.rb. In case you’re not aware of it, Bundler: Gemfiles has some tricks to point the gem to a different location, like path or git / github. That allows you to test your implementation end-to-end.It is fine (or actually very good) to submit a draft PR that points bundler.d/bmc.rb to a GitHub PR to redfish_client. Once the change in redfish_client is merged you can update the PR with the new minimum version and mark it as ready for review.

2 Likes

Hi @ekohl
Thank you very much for the detailed and thoughtful feedback — I really appreciate it.
Your comments on ETag semantics, fallback behavior, and the development workflow using bundler.d/bmc.rb were extremely helpful.

I will proceed with preparing the PRs for both redfish_client and the Smart Proxy using the two-layer approach you described.

Thank you.

1 Like

Hi @ekohl,

Thank you very much for the thorough review and thoughtful discussion.

Just to share the current status:
I have already started implementing this with the two-layer approach (Smart Proxy + redfish_client) proposed in the RFC.


1) Extending redfish-client-ruby

To add ETag / If-Match support to the Redfish client library, I submitted the following PR:

The goal is to provide an API that allows conditional PATCH using the retrieved ETag.

For reference, I’m sharing that I’m using sushy, the OpenStack Python-based Redfish client library, as an implementation reference:


2) Updating Smart Proxy to use the ETag-aware API

On the Smart Proxy side, I also submitted a draft PR to enable BootOrder updates for BMC implementations that require ETag-based conditional PATCH:

In the Smart Proxy PR, I temporarily point redfish_client to my fork/branch via bundler.d/bmc.rb, so the Smart Proxy changes can be tested end-to-end before the upstream gem is released. Once the redfish_client PR is merged and a new version is released, I’ll switch the dependency back to the released gem version.


Thanks again.
I’ll keep you posted as progress continues.

1 Like