Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

redfish

A Dart client for the DMTF Redfish API — the HTTPS interface most enterprise server BMCs have provided since roughly 2016.

Written against what BMCs actually send, which is not always what the specification says they will. Extracted from ServerBox, where it reaches a server's management controller when the host operating system is not answering.

Pure Dart. No Flutter dependency.

Not published to pub.dev yet. The API is still moving. Use a git or path dependency.

What it does

Protocol

  • GET, POST and PATCH, with If-Match from the ETag a fetch returned
  • 202 Accepted comes back as a task to poll, not as a result — see below
  • Sessions by default, basic auth on request, and basic automatically when the service offers no session collection
  • $expand where a service supports it, as an optimisation and never a requirement

Resources

  • Discovery: walks Systems and Chassis rather than building paths, because the ids differ per vendor
  • ComputerSystem: power state, model, serial, BIOS version, health
  • ComputerSystem.Reset, with the reset type negotiated against what the service says it allows
  • Chassis: sensors, out of either of the two models Redfish defines
  • Manager: the controller's own firmware, clock, network interfaces and reset — which reboots the BMC and not the server
  • LogService / LogEntry: the event log, with the SEL picked out of the several a manager publishes
  • Boot: reading an override and building the PATCH that sets or clears one
  • Inventory: processors, memory, drives, reduced to one shape

Not covered. Virtual media, UpdateService and firmware upload, AccountService, EventService subscriptions, message registries (a MessageId with no Message is passed through rather than resolved), Swordfish.

A 202 is not a result

Any modifying request may answer 202 with a task doing the work elsewhere. A client that treats every 2xx as completion reports success at the moment the work was queued.

final outcome = await client.post(request.target, request.body);
if (outcome case RedfishAccepted()) {
  final task = await client.awaitTask(outcome);
  if (!task.state.isSuccess) print(task.messages);
}

awaitTask returns the last state it saw. A task still running when the wait ends comes back running rather than as a failure: the work has not stopped, only the watching has.

Design

The split that matters is between the parts that need a BMC and the parts that do not.

resources.dart   JSON -> models. No IO.
sensors.dart     Readings, out of either sensor model. No IO.
discovery.dart   Walking a service. Takes a RedfishTransport.
client.dart      The only thing that opens a socket.
cert_pin.dart    TLS trust on first use, in the two halves the platform forces.

Everything above client takes a decoded JSON map and returns a model, so a vendor difference can be reproduced from a saved response without the hardware that produced it. That is not tidiness — it is the only way the differences in doc/vendors.md stay honest.

TLS

BMCs ship self-signed certificates, so there is nothing for a CA to vouch for. This library does not offer a way to accept any certificate. It offers trust on first review:

// Reads the certificate and sends nothing. Put it to a person.
final info = await fetchServerCert('10.0.0.9', 443);
print(info.prettyFingerprint);

final client = RedfishClient(
  baseUrl: 'https://10.0.0.9',
  user: 'ADMIN',
  password: '...',
  pinnedCertSha256: info.fingerprint,   // null refuses everything
);

The two halves are separate because the platform forces it: badCertificateCallback is bool Function(...), so nothing there can wait for an answer. Trusting whatever appears on the first request would be trust-on-first-use where the use already carries a password.

Usage

final client = RedfishClient(
  baseUrl: 'https://10.0.0.9',
  user: 'ADMIN',
  password: '...',
  pinnedCertSha256: pin,
);

try {
  final topology = await RedfishDiscovery(client).run();
  print(topology.system?.powerState);

  final request = ResetRequest.build(topology.system!, PowerIntent.gracefulShutdown);
  if (request != null) {
    await client.post(request.target, request.body);
  }
} finally {
  await client.close();   // releases the session
}

ResetRequest.build returns null when the service allows nothing that satisfies the intent. That is a real answer: an operation with nothing behind it should not be offered rather than offered and failing when pressed.

Testing against a service you do not have

mockups/ holds Redfish trees that can be served back with DMTF's Redfish-Mockup-Server:

tool/serve_mockup.sh                    # DMTF's public-rackmount1
tool/serve_mockup.sh mockups/real/foo   # a tree captured from real firmware

Contributions of mockups are more useful than bug reports. DMTF's Redfish-Mockup-Creator captures a live service into a folder — no credentials leave your network, and the result becomes a permanent fixture here. Scrub serial numbers and network configuration first.

Note the difference between the two kinds: DMTF's published mockups are reference examples and state that they "do not represent actual implementations". They test conformance to the specification. Mockups captured from real firmware are what catch the things in doc/vendors.md, none of which any specification predicts.

License

Apache-2.0. See LICENSE and NOTICE.

About

A Dart client for the DMTF Redfish API, written against what BMCs actually send

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages