Merge pull request #1 from chrismanivong/feat/redfish-client

Release 0.1.0: Redfish driver on ManagementControllerDriver
This commit is contained in:
christianmanivong authored and GitHub committed 2026-10-13 07:15:14 +02:00
commit c7d947a870
147 files changed
+9283 -5

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.1.0] - 2026-10-13
### Added
- `redfish` NAPALM driver (`RedfishDriver`) on `ManagementControllerDriver` from
napalm-device-types 4.7, registered as the `napalm.drivers` entry point `redfish`
- NAPALM getters for the BMC: `get_facts`, `get_interfaces`, `get_interfaces_ip`,
`get_environment` (fans, temperatures, power supplies; `cpu` and `memory` empty),
`get_users`, `get_ntp_servers`, `get_snmp_information`
- Role methods for the managed server: `get_power_state`, `set_power_state`,
`get_hardware_inventory`, `get_event_log`, `get_firmware_inventory`,
`get_management_protocols`; `reboot_host` resets the BMC (`Manager.Reset`)
- HTTPS transport on the DMTF `redfish` library: Redfish session login with the session
deleted on close, basic auth fallback only when sessions are unsupported, certificate
verification on by default (`verify_ssl` accepts a CA file), collections followed by
`@odata.id` including `nextLink` pages, links to other hosts refused
- Environment from `ThermalSubsystem`/`PowerSubsystem`/`Sensors` where present, legacy
`Thermal`/`Power` otherwise
- Pure parsers for every resource in `docs/redfish-map.md`
- Tests against DMTF Redfish mockups (public-rackmount1, public-localstorage,
public-telemetry) over mocked HTTP
### Known limitations
- Not yet run against real hardware; Supermicro X10 behaviour is from the design notes
- No `ipmi` fallback driver yet
- `get_config` is not implemented (a BMC has no configuration file)
+37 -2
View File
@@ -5,8 +5,11 @@ NAPALM driver for server **baseboard management controllers (BMCs)** over
boards that predate Redfish. Built on
[napalm-device-types](https://git.netork.io/NAPALM/napalm-device-types). Written for netOrk.
**Status: planning.** There is no code yet. This repository holds the design so far. The
first test device is a Supermicro X10DRL-i (BMC firmware 3.91, Redfish).
**Status: early development.** The `redfish` driver (`napalm_redfish.RedfishDriver`, a
`ManagementControllerDriver` from napalm-device-types) implements the NAPALM getters and
the role methods in [docs/redfish-map.md](docs/redfish-map.md), tested end to end against
DMTF mockups. It has not yet run against real hardware; the first test device is a
Supermicro X10DRL-i (BMC firmware 3.91, Redfish). The `ipmi` fallback does not exist yet.
## Why Redfish, and why not one driver per vendor
@@ -60,6 +63,38 @@ These follow the other drivers in the `NAPALM` organisation:
- `napalm-device-types` is not on PyPI: install it from
`git+https://git.netork.io/NAPALM/napalm-device-types.git` before this package.
## Usage
```python
import napalm
driver = napalm.get_network_driver("redfish")
with driver("bmc01.example.net", "netork", "secret",
optional_args={"verify_ssl": "/etc/netork/bmc-ca.pem"}) as bmc:
bmc.get_facts() # the BMC: firmware as os_version, its ports
bmc.get_power_state() # the server: {"state": "On", "actions": [...]}
bmc.get_hardware_inventory()
```
Optional args: `port` (443), `verify_ssl` (`True`, `False` or a CA file path; default
`True`), `auth` (`auto`, `session` or `basic`; `auto` uses a Redfish session and falls back
to basic auth only if the service cannot create sessions), `retries` (1).
`reboot_host` restarts the BMC, never the server; the server is switched through
`set_power_state`. `get_config` is not implemented: a BMC has no configuration file.
## Development
```bash
python -m venv .venv && . .venv/bin/activate
pip install git+https://git.netork.io/NAPALM/napalm-device-types.git
pip install -e '.[test]'
pytest
```
Fixtures from the DMTF mockups live under `tests/fixtures/dmtf-<mockup>/`, unmodified, with
the DMTF license and a `COMMANDS.txt` naming the source commit and request paths.
## License
Apache License 2.0, see [LICENSE](LICENSE).
+4 -3
View File
@@ -22,9 +22,10 @@ Collected 2026-10-13.
| `sushy-tools` (OpenStack) | Redfish emulator for tests | Apache-2.0 | active |
| `pyghmi` | IPMI over LAN, pure Python | Apache-2.0 | release 1.6.19, 2026-07-13 on PyPI (the GitHub mirror is archived) |
Choosing between the thin `redfish` client and `sushy` is open. The thin client keeps
quirk handling in this driver; `sushy` brings an object model but also its own assumptions
about Ironic's needs.
The transport uses the thin `redfish` client, which keeps quirk handling in this driver;
`sushy` brings an object model but also its own assumptions about Ironic's needs. Note that
the `redfish` library turns certificate verification off whenever it is not given a CA file,
so the transport always passes one when verification is on.
## Supermicro
+6
View File
@@ -0,0 +1,6 @@
"""NAPALM driver for server BMCs over DMTF Redfish."""
from napalm_redfish.driver import RedfishDriver
__version__ = "0.1.0"
__all__ = ["RedfishDriver"]
+542
View File
@@ -0,0 +1,542 @@
"""NAPALM driver for server BMCs over DMTF Redfish.
The device is the BMC. NAPALM's getters describe the BMC (its ports, users, services);
the ``ManagementControllerDriver`` methods describe and control the server it manages.
``reboot_host`` restarts the BMC (``Manager.Reset``), never the server.
Optional args
-------------
port : int
HTTPS port (default 443).
verify_ssl : bool or str
``True`` (default) verifies the certificate against the CA bundle of ``requests``,
a path verifies against that CA file, ``False`` turns verification off (self-signed
BMC certificates are common).
auth : str
``auto`` (default: session, basic auth if the service cannot create sessions),
``session`` or ``basic``.
retries : int
Extra attempts after a connection error (default 1).
The managed server is the first member of ``Systems``; its BMC is the first manager in
its ``Links.ManagedBy`` and its chassis the first in its ``Links.Chassis`` (the first
member of ``Managers``/``Chassis`` when the links are missing).
"""
from __future__ import annotations
import logging
from datetime import datetime, timezone
from typing import Any
from napalm.base.exceptions import ConnectionException
from napalm_device_types import ManagementControllerDriver
from napalm_device_types.base import FingerprintRule, PortSpec
from napalm_device_types.models import POWER_ACTIONS
from napalm_redfish.parsers import (
build_environment,
environment_sources,
parse_account,
parse_action_info,
parse_drive,
parse_ethernet_interface,
parse_fan,
parse_firmware,
parse_log_entry,
parse_log_service,
parse_manager,
parse_memory,
parse_network_protocol,
parse_power,
parse_power_state,
parse_power_subsystem,
parse_power_supply,
parse_processor,
parse_reset_action,
parse_sensor,
parse_service_root,
parse_simple_storage,
parse_storage,
parse_system,
parse_thermal,
parse_thermal_metrics,
parse_thermal_subsystem,
to_napalm_users,
)
from napalm_redfish.parsers.common import link, link_list, odata_id
from napalm_redfish.transport import RedfishError, RedfishNotFound, RedfishTransport
LOG = logging.getLogger(__name__)
_JsonDict = dict[str, Any]
#: ``get_management_protocols`` key -> ManagerNetworkProtocol property.
_PROTOCOLS = {
"ipmi": "IPMI",
"ssh": "SSH",
"http": "HTTP",
"https": "HTTPS",
"snmp": "SNMP",
"kvm": "KVMIP",
"virtual_media": "VirtualMedia",
}
_SEVERITIES = ("OK", "Warning", "Critical")
def _timestamp(created: str | None) -> float | None:
"""Unix time of a Redfish ``Created`` value; a time without offset counts as UTC."""
if not created:
return None
try:
when = datetime.fromisoformat(created.replace("Z", "+00:00"))
except ValueError:
return None
if when.tzinfo is None:
when = when.replace(tzinfo=timezone.utc)
return when.timestamp()
def _status(item: _JsonDict) -> str:
"""Role ``status``: ``Absent`` for an empty slot, else the Redfish health."""
if not item.get("present", True):
return "Absent"
return item.get("health") or ""
class RedfishDriver(ManagementControllerDriver):
"""NAPALM driver for Redfish BMCs (Supermicro, iDRAC, iLO, XCC and others)."""
platform = "redfish"
DRIVER_NAME = "redfish"
#: Every Redfish service answers ``GET /redfish/v1/`` without credentials.
PORT_SPECS = [PortSpec("https", 443, ("/redfish/v1/",))]
HTTP_FINGERPRINT = [FingerprintRule("redfishversion", 10.0),
FingerprintRule("#serviceroot.", 5.0)]
#: A BMC restart takes a few minutes before Redfish answers again.
REBOOT_SETTLE_SECONDS = 180
def __init__(
self,
hostname: str,
username: str,
password: str,
timeout: int = 60,
optional_args: _JsonDict | None = None,
) -> None:
self.hostname = hostname
self.username = username
self.password = password
self.timeout = timeout
self.optional_args: _JsonDict = optional_args or {}
self.transport = RedfishTransport(
hostname,
username,
password,
port=self.optional_args.get("port"),
verify=self.optional_args.get("verify_ssl", True),
auth=self.optional_args.get("auth", "auto"),
timeout=timeout,
retries=self.optional_args.get("retries", 1),
)
self._cache: _JsonDict = {}
# --- connection ----------------------------------------------------------------------
def open(self) -> None:
try:
self.transport.open()
except RedfishError as exc:
raise ConnectionException(f"Cannot connect to Redfish at {self.hostname}: {exc}") \
from exc
def close(self) -> None:
self.transport.close()
self._cache = {}
def is_alive(self) -> _JsonDict:
try:
self.transport.get("/redfish/v1/")
except Exception:
return {"is_alive": False}
return {"is_alive": True}
# --- discovery (cached per connection) -------------------------------------------------
def _cached(self, key: str, load):
if key not in self._cache:
self._cache[key] = load()
return self._cache[key]
def _root(self) -> _JsonDict:
return self._cached("root", lambda: parse_service_root(self.transport.root))
def _first(self, collection: str) -> _JsonDict:
path = self._root()["links"].get(collection)
if path is None:
raise RedfishError(f"the service root links no {collection}")
members = self.transport.members(path, limit=1)
if not members:
raise RedfishError(f"{path} has no members")
return members[0]
def _system_raw(self) -> _JsonDict:
return self._cached("system", lambda: self._first("Systems"))
def _system_path(self) -> str:
return odata_id(self._system_raw()) or ""
def _linked_or_first(self, key: str, links_key: str, collection: str) -> _JsonDict:
def load() -> _JsonDict:
linked = link_list(self._system_raw(), "Links", links_key)
if linked:
return self.transport.get(linked[0])
return self._first(collection)
return self._cached(key, load)
def _manager_raw(self) -> _JsonDict:
return self._linked_or_first("manager", "ManagedBy", "Managers")
def _chassis_raw(self) -> _JsonDict:
return self._linked_or_first("chassis", "Chassis", "Chassis")
def _members_of(self, resource: _JsonDict, key: str) -> list[_JsonDict]:
"""Members of the collection ``resource[key]`` links; [] if not linked."""
path = link(resource, key)
return self.transport.members(path) if path else []
def _service(self, name: str) -> _JsonDict | None:
path = self._root()["links"].get(name)
return self.transport.get_optional(path) if path else None
def _network_protocol(self) -> _JsonDict:
raw = self.transport.follow(self._manager_raw(), "NetworkProtocol")
return parse_network_protocol(raw or {})
def _manager_interfaces(self) -> list[_JsonDict]:
return [parse_ethernet_interface(raw)
for raw in self._members_of(self._manager_raw(), "EthernetInterfaces")]
# --- NAPALM getters ----------------------------------------------------------------
def get_facts(self) -> _JsonDict:
system = parse_system(self._system_raw())
manager = parse_manager(self._manager_raw())
interfaces = self._manager_interfaces()
protocol = self._network_protocol()
hostname = (protocol["hostname"]
or next((i["hostname"] for i in interfaces if i["hostname"]), None)
or self.hostname)
fqdn = (protocol["fqdn"]
or next((i["fqdn"] for i in interfaces if i["fqdn"]), None)
or hostname)
return {
"uptime": -1.0, # Redfish has no uptime
"vendor": system["manufacturer"] or "",
"model": system["model"] or "",
"hostname": hostname,
"fqdn": fqdn,
"os_version": manager["firmware_version"] or "",
"serial_number": system["serial_number"] or "",
"interface_list": sorted(i["name"] for i in interfaces if i["name"]),
}
def get_interfaces(self) -> _JsonDict:
return {i["name"]: i["interface"] for i in self._manager_interfaces() if i["name"]}
def get_interfaces_ip(self) -> _JsonDict:
result = {}
for iface in self._manager_interfaces():
families = {f: iface[f] for f in ("ipv4", "ipv6") if iface[f]}
if iface["name"] and families:
result[iface["name"]] = families
return result
def get_environment(self) -> _JsonDict:
"""Fans, temperatures and power supplies of the chassis; ``cpu`` and ``memory`` are
empty, since a BMC does not know the host OS's load."""
sources = environment_sources(self._chassis_raw())
fans: _JsonDict = {}
temperature: _JsonDict = {}
power: _JsonDict = {}
thermal = sources["thermal"]
if thermal and thermal["schema"] == "Thermal":
legacy = parse_thermal(self.transport.get(thermal["path"]))
fans, temperature = legacy["fans"], legacy["temperature"]
elif thermal:
subsystem = parse_thermal_subsystem(self.transport.get(thermal["path"]))
parsed_fans = [parse_fan(raw) for raw in self.transport.members(
subsystem["fans_path"])] if subsystem["fans_path"] else []
if sources["sensors"]:
sensors = [parse_sensor(raw)
for raw in self.transport.members(sources["sensors"])]
temperature = build_environment(sensors=sensors)["temperature"]
elif subsystem["thermal_metrics_path"]:
metrics = self.transport.get_optional(subsystem["thermal_metrics_path"])
temperature = parse_thermal_metrics(metrics or {})["temperature"]
fans = build_environment(fans=parsed_fans)["fans"]
supply = sources["power"]
if supply and supply["schema"] == "Power":
power = parse_power(self.transport.get(supply["path"]))["power"]
elif supply:
psus = [parse_power_supply(raw, self.transport.follow(raw, "Metrics"))
for raw in self._power_supplies_raw(supply["path"])]
power = build_environment(power_supplies=psus)["power"]
return {"fans": fans, "temperature": temperature, "power": power,
"cpu": {}, "memory": {}}
def _power_supplies_raw(self, subsystem_path: str) -> list[_JsonDict]:
subsystem = parse_power_subsystem(self.transport.get(subsystem_path))
path = subsystem["power_supplies_path"]
return self.transport.members(path) if path else []
def get_users(self) -> _JsonDict:
service = self._service("AccountService")
if service is None:
return {}
return to_napalm_users(parse_account(raw) for raw in self._members_of(service,
"Accounts"))
def get_ntp_servers(self) -> _JsonDict:
return {server: {} for server in self._network_protocol()["ntp"]["servers"]}
def get_snmp_information(self) -> _JsonDict:
"""SNMP state from ManagerNetworkProtocol. Redfish hides community strings, so the
``community`` keys are the communities' names; contact and location are not in
Redfish."""
snmp = self._network_protocol()["snmp"]
communities = {}
if snmp["enabled"]:
for community in snmp["communities"]:
if community["name"]:
mode = "rw" if community["access"] == "Full" else "ro"
communities[community["name"]] = {"acl": "", "mode": mode}
return {"chassis_id": "", "community": communities, "contact": "", "location": ""}
# --- role: power -------------------------------------------------------------------
def _accepted_actions(self, system: _JsonDict) -> list[str]:
"""The PowerAction values the BMC accepts for ``system`` in its current state."""
reset = parse_reset_action(system)
if reset["target"] is None:
return []
allowed = reset["allowed"]
if allowed is None and reset["action_info"]:
info = self.transport.get_optional(reset["action_info"])
allowed = parse_action_info(info) if info else None
if allowed is None:
# Not advertised: assume the standard values; the BMC rejects what it lacks.
allowed = list(POWER_ACTIONS)
actions = [a for a in POWER_ACTIONS if a in allowed]
state = parse_power_state(system)
if state == "Off":
return [a for a in actions if a == "On"]
if state == "On":
return [a for a in actions if a != "On"]
return actions
def get_power_state(self) -> _JsonDict:
system = self.transport.get(self._system_path())
return {"state": parse_power_state(system) or "",
"actions": self._accepted_actions(system)}
def set_power_state(self, action: str) -> None:
if action not in POWER_ACTIONS:
raise ValueError(f"{action!r} is not one of {', '.join(POWER_ACTIONS)}")
system = self.transport.get(self._system_path())
target = parse_reset_action(system)["target"]
if target is None:
raise RuntimeError(f"{self.hostname}: the system offers no reset action")
accepted = self._accepted_actions(system)
if action not in accepted:
raise RuntimeError(
f"{self.hostname}: {action} not accepted while the server is "
f"{parse_power_state(system) or 'in an unknown state'} "
f"(accepted: {', '.join(accepted) or 'none'})")
try:
self.transport.post(target, {"ResetType": action})
except RedfishError as exc:
raise RuntimeError(f"{self.hostname}: {action} refused: {exc}") from exc
def reboot_host(self) -> None:
"""Restart the BMC (``Manager.Reset``, graceful when offered). The server keeps
running; the Redfish session ends with the restart."""
manager = self.transport.get(odata_id(self._manager_raw()) or "")
reset = parse_reset_action(manager, "#Manager.Reset")
if reset["target"] is None:
raise RuntimeError(f"{self.hostname}: the BMC offers no reset action")
body: _JsonDict = {}
if reset["allowed"] is not None:
reset_type = next((t for t in ("GracefulRestart", "ForceRestart")
if t in reset["allowed"]), None)
if reset_type is None:
raise RuntimeError(f"{self.hostname}: BMC reset types "
f"{reset['allowed']} include no restart")
body["ResetType"] = reset_type
try:
self.transport.post(reset["target"], body)
except RedfishError as exc:
raise RuntimeError(f"{self.hostname}: BMC reset refused: {exc}") from exc
# --- role: inventory -----------------------------------------------------------------
def get_hardware_inventory(self) -> _JsonDict:
raw = self._system_raw()
system = parse_system(raw)
return {
"manufacturer": system["manufacturer"] or "",
"model": system["model"] or "",
"serial_number": system["serial_number"] or "",
"system_uuid": (system["uuid"] or "").lower(),
"cpus": self._cpus(raw),
"memory": self._dimms(raw),
"drives": self._drives(raw),
"power_supplies": self._psus(),
"nics": self._nics(raw),
}
def _cpus(self, system: _JsonDict) -> list[_JsonDict]:
cpus = []
for raw in self._members_of(system, "Processors"):
cpu = parse_processor(raw)
if cpu["processor_type"] not in (None, "CPU"):
continue # GPUs, FPGAs, accelerators
cpus.append({"socket": cpu["socket"] or cpu["id"] or "", "model": cpu["model"] or "",
"cores": cpu["cores"], "threads": cpu["threads"],
"status": _status(cpu)})
return cpus
def _dimms(self, system: _JsonDict) -> list[_JsonDict]:
dimms = []
for raw in self._members_of(system, "Memory"):
dimm = parse_memory(raw)
size = dimm["capacity_mib"]
dimms.append({
"slot": dimm["location"] or dimm["id"] or "",
"size_bytes": size * 1024 * 1024 if size else None,
"type": dimm["memory_device_type"] or "",
"speed_mts": dimm["speed_mhz"], # Redfish: MHz or MT/s "as appropriate"
"part_number": dimm["part_number"] or "",
"serial_number": dimm["serial_number"] or "",
"status": _status(dimm),
})
return dimms
def _drives(self, system: _JsonDict) -> list[_JsonDict]:
parsed: list[_JsonDict] = []
if link(system, "Storage"):
seen: set[str] = set()
for storage in self._members_of(system, "Storage"):
for path in parse_storage(storage)["drive_paths"]:
if path not in seen:
seen.add(path)
drive = self.transport.get_optional(path)
if drive is not None:
parsed.append(parse_drive(drive))
else:
for storage in self._members_of(system, "SimpleStorage"):
parsed.extend(parse_simple_storage(storage)["drives"])
return [{
"slot": d["name"] or d["id"] or "",
"model": d["model"] or "",
"serial_number": d["serial_number"] or "",
"size_bytes": d["capacity_bytes"],
"media": d["media_type"] or "",
"protocol": d["protocol"] or "",
"status": _status(d),
} for d in parsed]
def _psus(self) -> list[_JsonDict]:
source = environment_sources(self._chassis_raw())["power"]
if source is None:
return []
if source["schema"] == "Power":
items = self.transport.get(source["path"]).get("PowerSupplies") or []
else:
items = self._power_supplies_raw(source["path"])
psus = []
for raw in items:
if not isinstance(raw, dict):
continue
psu = parse_power_supply(raw)
psus.append({
"slot": psu["name"],
"model": psu["model"] or "",
"serial_number": psu["serial_number"] or "",
"capacity_watts": int(psu["capacity"]) if psu["capacity"] >= 0 else None,
"status": _status(psu),
})
return psus
def _nics(self, system: _JsonDict) -> list[_JsonDict]:
nics = []
for raw in self._members_of(system, "EthernetInterfaces"):
nic = parse_ethernet_interface(raw)
speed = nic["interface"]["speed"]
link_up = {"LinkUp": True, "LinkDown": False, "NoLink": False}.get(
nic["link_status"] or "")
nics.append({
"name": nic["name"] or "",
"mac_address": nic["interface"]["mac_address"].lower(),
"speed_mbps": int(speed) if speed >= 0 else None,
"link_up": link_up,
})
return nics
# --- role: event log, firmware, protocols --------------------------------------------
def get_event_log(self, limit: int = 500) -> list[_JsonDict]:
"""Entries of every log service of the system and of the BMC, newest first.
Where the SEL lives varies by vendor, so all of them are read; entries without a
time sort last.
"""
entries: list[_JsonDict] = []
for owner in (self._system_raw(), self._manager_raw()):
try:
services = self._members_of(owner, "LogServices")
except RedfishNotFound:
continue
for raw_service in services:
service = parse_log_service(raw_service)
if not service["entries_path"]:
continue
try:
raw_entries = self.transport.members(service["entries_path"])
except RedfishNotFound:
LOG.warning("%s: log %s has no entries collection", self.hostname,
service["id"])
continue
for raw in raw_entries:
entry = parse_log_entry(raw)
severity = entry["severity"]
entries.append({
"id": odata_id(raw) or f"{service['id']}/{entry['id']}",
"timestamp": _timestamp(entry["created"]),
"severity": severity if severity in _SEVERITIES else "OK",
"message": entry["message"] or "",
})
entries.sort(key=lambda e: (e["timestamp"] is not None, e["timestamp"] or 0.0),
reverse=True)
return entries[:limit]
def get_firmware_inventory(self) -> list[_JsonDict]:
service = self._service("UpdateService")
if service is None:
return []
items = [parse_firmware(raw) for raw in self._members_of(service, "FirmwareInventory")]
return [{"name": fw["name"] or fw["id"] or "", "version": fw["version"] or "",
"updateable": fw["updateable"]} for fw in items]
def get_management_protocols(self) -> _JsonDict:
"""What the BMC listens on. A protocol the BMC does not report counts as off."""
protocols = self._network_protocol()["protocols"]
result = {}
for key, name in _PROTOCOLS.items():
entry = protocols.get(name) or {}
result[key] = {"enabled": bool(entry.get("enabled")), "port": entry.get("port")}
return result
+62
View File
@@ -0,0 +1,62 @@
"""Pure parsers: one Redfish resource (a dict) in, plain Python values out.
No I/O here. The transport fetches, these functions read; tests run them on recorded
responses under ``tests/fixtures/``.
"""
from .accounts import parse_account, to_napalm_users
from .environment import (
build_environment,
environment_sources,
parse_fan,
parse_power,
parse_power_subsystem,
parse_power_supply,
parse_sensor,
parse_thermal,
parse_thermal_metrics,
parse_thermal_subsystem,
)
from .firmware import parse_firmware
from .inventory import (
parse_drive,
parse_memory,
parse_processor,
parse_simple_storage,
parse_storage,
)
from .logs import parse_log_entry, parse_log_service
from .manager import parse_ethernet_interface, parse_manager, parse_network_protocol
from .service_root import parse_service_root
from .system import parse_action_info, parse_power_state, parse_reset_action, parse_system
__all__ = [
"build_environment",
"environment_sources",
"parse_account",
"parse_action_info",
"parse_drive",
"parse_ethernet_interface",
"parse_fan",
"parse_firmware",
"parse_log_entry",
"parse_log_service",
"parse_manager",
"parse_memory",
"parse_network_protocol",
"parse_power",
"parse_power_state",
"parse_power_subsystem",
"parse_power_supply",
"parse_processor",
"parse_reset_action",
"parse_sensor",
"parse_service_root",
"parse_simple_storage",
"parse_storage",
"parse_system",
"parse_thermal",
"parse_thermal_metrics",
"parse_thermal_subsystem",
"to_napalm_users",
]
+40
View File
@@ -0,0 +1,40 @@
"""AccountService accounts (``AccountService/Accounts/*``)."""
from __future__ import annotations
from typing import Any, Iterable, Mapping
from .common import boolean, text
# Privilege level for NAPALM's get_users, from the Redfish standard roles.
ROLE_LEVELS = {"Administrator": 15, "Operator": 5, "ReadOnly": 1}
def parse_account(account: Mapping[str, Any]) -> dict[str, Any] | None:
"""One account; ``None`` for an unused slot (no UserName), as iDRAC and Supermicro list."""
username = text(account.get("UserName"))
if username is None:
return None
types = account.get("AccountTypes")
return {
"id": text(account.get("Id")),
"username": username,
"role_id": text(account.get("RoleId")),
"enabled": boolean(account.get("Enabled")),
"locked": boolean(account.get("Locked")),
"account_types": [str(t) for t in types] if isinstance(types, list) else [],
}
def to_napalm_users(accounts: Iterable[Mapping[str, Any] | None]) -> dict[str, dict[str, Any]]:
"""NAPALM ``get_users`` from parsed accounts. Unknown (OEM) roles get level 0."""
users = {}
for acc in accounts:
if not acc:
continue
users[acc["username"]] = {
"level": ROLE_LEVELS.get(acc.get("role_id") or "", 0),
"password": "",
"sshkeys": [],
}
return users
+134
View File
@@ -0,0 +1,134 @@
"""Helpers shared by the parsers. All pure: dict in, plain values out."""
from __future__ import annotations
from typing import Any, Mapping
# DMI defaults that board vendors leave in place (Supermicro boards ship most of them).
PLACEHOLDERS = frozenset({
"to be filled by o.e.m.",
"default string",
"not specified",
"not applicable",
"n/a",
"none",
"0123456789",
"system serial number",
"system product name",
"system manufacturer",
})
def text(value: Any) -> str | None:
"""A stripped string, ``None`` for missing or blank values."""
if value is None:
return None
value = str(value).strip()
return value or None
def ident(value: Any) -> str | None:
"""Like ``text``, but DMI placeholder strings count as missing too."""
value = text(value)
if value is None or value.lower() in PLACEHOLDERS:
return None
return value
def number(value: Any) -> float | None:
"""A float, ``None`` if the value is missing or not a number."""
if isinstance(value, bool) or value is None:
return None
try:
return float(value)
except (TypeError, ValueError):
return None
def integer(value: Any) -> int | None:
if isinstance(value, bool) or value is None:
return None
try:
return int(value)
except (TypeError, ValueError):
return None
def boolean(value: Any) -> bool | None:
return value if isinstance(value, bool) else None
def odata_id(ref: Any) -> str | None:
"""The ``@odata.id`` of a reference object, or ``None``."""
if isinstance(ref, Mapping):
return text(ref.get("@odata.id"))
return None
def link(resource: Mapping[str, Any], *keys: str) -> str | None:
"""Follow ``keys`` into nested objects and return the ``@odata.id`` found there."""
value: Any = resource
for key in keys:
if not isinstance(value, Mapping):
return None
value = value.get(key)
return odata_id(value)
def links(resource: Mapping[str, Any]) -> dict[str, str]:
"""All top-level properties of ``resource`` that are single references."""
found = {}
for key, value in resource.items():
if key.startswith("@") or key == "Links":
continue
path = odata_id(value)
if path is not None:
found[key] = path
return found
def link_list(resource: Mapping[str, Any], *keys: str) -> list[str]:
"""The ``@odata.id`` of each reference in the array at ``keys``."""
value: Any = resource
for key in keys:
if not isinstance(value, Mapping):
return []
value = value.get(key)
if not isinstance(value, list):
return []
return [path for path in (odata_id(ref) for ref in value) if path is not None]
def status(resource: Mapping[str, Any]) -> tuple[str | None, str | None]:
"""``(Status.State, Status.Health)``."""
st = resource.get("Status")
if not isinstance(st, Mapping):
return None, None
return text(st.get("State")), text(st.get("Health"))
def present(resource: Mapping[str, Any]) -> bool:
return status(resource)[0] != "Absent"
def location_label(resource: Mapping[str, Any]) -> str | None:
"""``Location.PartLocation.ServiceLabel``, the printed label of a slot or bay."""
loc = resource.get("Location")
if not isinstance(loc, Mapping):
return None
part = loc.get("PartLocation")
if not isinstance(part, Mapping):
return None
return text(part.get("ServiceLabel"))
def unique_name(names: Mapping[str, Any], name: str, suffix: Any) -> str:
"""``name``, or ``name (suffix)`` if it is already taken."""
if name not in names:
return name
candidate = f"{name} ({suffix})"
n = 2
while candidate in names:
candidate = f"{name} ({suffix}-{n})"
n += 1
return candidate
+309
View File
@@ -0,0 +1,309 @@
"""Chassis environment: legacy Thermal/Power, or ThermalSubsystem/PowerSubsystem/Sensors.
Services from Redfish 2020.4 on link ``ThermalSubsystem``, ``PowerSubsystem`` and
``Sensors`` from the Chassis and deprecate ``Thermal`` and ``Power``; older ones (such as
Supermicro X10) have only the latter. ``environment_sources`` picks per part, and both
paths end in the same shape, the parts of NAPALM's ``get_environment``:
- ``fans``: ``{name: {"status": bool}}``
- ``temperature``: ``{name: {"temperature": float, "is_alert": bool, "is_critical": bool}}``
- ``power``: ``{name: {"status": bool, "capacity": float, "output": float}}`` (-1.0 unknown)
plus ``voltages`` (``{name: {"voltage", "is_alert", "is_critical"}}``) and
``consumed_watts`` for the whole chassis. Absent parts and sensors without a reading are
left out.
"""
from __future__ import annotations
from typing import Any, Iterable, Mapping
from .common import link, number, present, status, text, unique_name
_SENSOR_TYPES_BY_UNITS = {"Cel": "Temperature", "V": "Voltage", "W": "Power", "A": "Current",
"RPM": "Rotational", "%": "Percent", "Hz": "Frequency"}
def environment_sources(chassis: Mapping[str, Any]) -> dict[str, Any]:
"""Which resources to read for a Chassis, preferring the newer ones per part.
Returns ``{"thermal": {"schema", "path"} | None, "power": ... | None,
"sensors": path | None}``.
"""
def pick(new: str, old: str) -> dict[str, str] | None:
for schema in (new, old):
path = link(chassis, schema)
if path is not None:
return {"schema": schema, "path": path}
return None
return {
"thermal": pick("ThermalSubsystem", "Thermal"),
"power": pick("PowerSubsystem", "Power"),
"sensors": link(chassis, "Sensors"),
}
def _ok(resource: Mapping[str, Any]) -> bool:
"""Working: enabled (or no state given) and healthy (or no health given)."""
state, health = status(resource)
return state in (None, "Enabled") and health in (None, "OK")
def _health_flags(resource: Mapping[str, Any]) -> tuple[bool, bool]:
_, health = status(resource)
return health in ("Warning", "Critical"), health == "Critical"
def _legacy_flags(item: Mapping[str, Any], reading: float) -> tuple[bool, bool]:
"""Alert/critical from Thermal/Power v1 threshold properties and health."""
alert, critical = _health_flags(item)
def beyond(suffix: str) -> bool:
upper = number(item.get(f"UpperThreshold{suffix}"))
lower = number(item.get(f"LowerThreshold{suffix}"))
return (upper is not None and reading >= upper) or (lower is not None and reading <= lower)
if beyond("Critical") or beyond("Fatal"):
return True, True
if beyond("NonCritical"):
alert = True
return alert, critical
def _sensor_flags(sensor: Mapping[str, Any], reading: float) -> tuple[bool, bool]:
"""Alert/critical from Sensor ``Thresholds`` and health."""
alert, critical = _health_flags(sensor)
thresholds = sensor.get("Thresholds") if isinstance(sensor.get("Thresholds"), Mapping) else {}
def beyond(level: str) -> bool:
def limit(name: str) -> float | None:
t = thresholds.get(name)
return number(t.get("Reading")) if isinstance(t, Mapping) else None
upper, lower = limit(f"Upper{level}"), limit(f"Lower{level}")
return (upper is not None and reading >= upper) or (lower is not None and reading <= lower)
if beyond("Critical") or beyond("Fatal"):
return True, True
if beyond("Caution"):
alert = True
return alert, critical
def _name(item: Mapping[str, Any], *keys: str) -> str:
for key in keys:
name = text(item.get(key))
if name:
return name
return text(item.get("MemberId")) or text(item.get("Id")) or "unknown"
def _members(resource: Mapping[str, Any], key: str) -> Iterable[tuple[int, Mapping[str, Any]]]:
items = resource.get(key)
for index, item in enumerate(items if isinstance(items, list) else []):
if isinstance(item, Mapping):
yield index, item
# --- legacy -------------------------------------------------------------------------------
def parse_thermal(thermal: Mapping[str, Any]) -> dict[str, Any]:
"""``Chassis/{id}/Thermal``: ``fans`` and ``temperature``."""
temperature: dict[str, Any] = {}
for index, item in _members(thermal, "Temperatures"):
reading = number(item.get("ReadingCelsius"))
if reading is None or not present(item):
continue
alert, critical = _legacy_flags(item, reading)
name = unique_name(temperature, _name(item, "Name"), item.get("MemberId", index))
temperature[name] = {"temperature": reading, "is_alert": alert, "is_critical": critical}
fans: dict[str, Any] = {}
for index, item in _members(thermal, "Fans"):
if not present(item):
continue
name = unique_name(fans, _name(item, "Name", "FanName"), item.get("MemberId", index))
fans[name] = {"status": _ok(item)}
return {"fans": fans, "temperature": temperature}
def parse_power(power: Mapping[str, Any]) -> dict[str, Any]:
"""``Chassis/{id}/Power``: ``power`` (supplies), ``voltages``, ``consumed_watts``."""
supplies: dict[str, Any] = {}
for index, item in _members(power, "PowerSupplies"):
psu = parse_power_supply(item)
if not psu["present"]:
continue
name = unique_name(supplies, psu["name"], item.get("MemberId", index))
supplies[name] = {"status": psu["status"], "capacity": psu["capacity"],
"output": psu["output"]}
voltages: dict[str, Any] = {}
for index, item in _members(power, "Voltages"):
reading = number(item.get("ReadingVolts"))
if reading is None or not present(item):
continue
alert, critical = _legacy_flags(item, reading)
name = unique_name(voltages, _name(item, "Name"), item.get("MemberId", index))
voltages[name] = {"voltage": reading, "is_alert": alert, "is_critical": critical}
consumed = None
for _, item in _members(power, "PowerControl"):
watts = number(item.get("PowerConsumedWatts"))
if watts is not None:
consumed = (consumed or 0.0) + watts
return {"power": supplies, "voltages": voltages, "consumed_watts": consumed}
# --- ThermalSubsystem, PowerSubsystem, Sensors --------------------------------------------
def parse_thermal_subsystem(subsystem: Mapping[str, Any]) -> dict[str, Any]:
"""``Chassis/{id}/ThermalSubsystem``: where the fans and metrics are."""
state, health = status(subsystem)
return {
"fans_path": link(subsystem, "Fans"),
"thermal_metrics_path": link(subsystem, "ThermalMetrics"),
"state": state,
"health": health,
}
def parse_power_subsystem(subsystem: Mapping[str, Any]) -> dict[str, Any]:
"""``Chassis/{id}/PowerSubsystem``: where the supplies are, total capacity."""
state, health = status(subsystem)
return {
"power_supplies_path": link(subsystem, "PowerSupplies"),
"capacity_watts": number(subsystem.get("CapacityWatts")),
"state": state,
"health": health,
}
def _excerpt(resource: Mapping[str, Any], key: str, field: str = "Reading") -> float | None:
value = resource.get(key)
return number(value.get(field)) if isinstance(value, Mapping) else None
def parse_fan(fan: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``ThermalSubsystem/Fans``."""
state, health = status(fan)
rpm = _excerpt(fan, "SpeedPercent", "SpeedRPM")
return {
"id": text(fan.get("Id")),
"name": _name(fan, "Name"),
"present": present(fan),
"status": _ok(fan),
"speed_rpm": int(rpm) if rpm is not None else None,
"speed_percent": _excerpt(fan, "SpeedPercent"),
"state": state,
"health": health,
}
def parse_power_supply(supply: Mapping[str, Any],
metrics: Mapping[str, Any] | None = None) -> dict[str, Any]:
"""One member of ``PowerSubsystem/PowerSupplies``, with its ``Metrics`` if read.
An item of the legacy ``Power.PowerSupplies`` array parses too (``MemberId`` as id,
``LastPowerOutputWatts`` as output).
"""
state, health = status(supply)
capacity = number(supply.get("PowerCapacityWatts"))
output = _excerpt(metrics, "OutputPowerWatts") if metrics else None
if output is None:
output = number(supply.get("LastPowerOutputWatts"))
return {
"id": text(supply.get("Id")) or text(supply.get("MemberId")),
"name": _name(supply, "Name"),
"present": present(supply),
"status": _ok(supply),
"capacity": capacity if capacity is not None else -1.0,
"output": output if output is not None else -1.0,
"manufacturer": text(supply.get("Manufacturer")),
"model": text(supply.get("Model")),
"serial_number": text(supply.get("SerialNumber")),
"part_number": text(supply.get("PartNumber")),
"firmware_version": text(supply.get("FirmwareVersion")),
"state": state,
"health": health,
}
def parse_sensor(sensor: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``Chassis/{id}/Sensors``.
``reading_type`` falls back to one derived from ``ReadingUnits`` (some sensors, such as
temperatures in the DMTF mockups, carry only units).
"""
state, health = status(sensor)
units = text(sensor.get("ReadingUnits"))
reading = number(sensor.get("Reading"))
alert, critical = _sensor_flags(sensor, reading) if reading is not None else (False, False)
return {
"id": text(sensor.get("Id")),
"name": _name(sensor, "Name"),
"reading_type": text(sensor.get("ReadingType")) or _SENSOR_TYPES_BY_UNITS.get(units or ""),
"reading": reading,
"units": units,
"physical_context": text(sensor.get("PhysicalContext")),
"electrical_context": text(sensor.get("ElectricalContext")),
"state": state,
"health": health,
"is_alert": alert,
"is_critical": critical,
}
def parse_thermal_metrics(metrics: Mapping[str, Any]) -> dict[str, Any]:
"""``ThermalSubsystem/ThermalMetrics``: temperatures without thresholds.
A fallback for services with a ThermalSubsystem but no Sensors collection.
"""
temperature: dict[str, Any] = {}
for index, item in _members(metrics, "TemperatureReadingsCelsius"):
reading = number(item.get("Reading"))
if reading is None:
continue
name = unique_name(temperature, _name(item, "DeviceName"), index)
temperature[name] = {"temperature": reading, "is_alert": False, "is_critical": False}
return {"temperature": temperature}
def build_environment(*, fans: Iterable[Mapping[str, Any]] = (),
power_supplies: Iterable[Mapping[str, Any]] = (),
sensors: Iterable[Mapping[str, Any]] = ()) -> dict[str, Any]:
"""Combine parsed fans, power supplies and sensors into the shape of the legacy parsers.
``consumed_watts`` comes from the chassis total power sensor
(``ReadingType`` Power, ``ElectricalContext`` Total, ``PhysicalContext`` Chassis).
"""
env: dict[str, Any] = {"fans": {}, "temperature": {}, "power": {}, "voltages": {},
"consumed_watts": None}
for fan in fans:
if fan["present"]:
name = unique_name(env["fans"], fan["name"], fan["id"])
env["fans"][name] = {"status": fan["status"]}
for psu in power_supplies:
if psu["present"]:
name = unique_name(env["power"], psu["name"], psu["id"])
env["power"][name] = {"status": psu["status"], "capacity": psu["capacity"],
"output": psu["output"]}
for sensor in sensors:
reading = sensor["reading"]
if reading is None:
continue
flags = {"is_alert": sensor["is_alert"], "is_critical": sensor["is_critical"]}
if sensor["reading_type"] == "Temperature":
name = unique_name(env["temperature"], sensor["name"], sensor["id"])
env["temperature"][name] = {"temperature": reading, **flags}
elif sensor["reading_type"] == "Voltage":
name = unique_name(env["voltages"], sensor["name"], sensor["id"])
env["voltages"][name] = {"voltage": reading, **flags}
elif (sensor["reading_type"] == "Power" and sensor["electrical_context"] == "Total"
and sensor["physical_context"] == "Chassis" and env["consumed_watts"] is None):
env["consumed_watts"] = reading
return env
+23
View File
@@ -0,0 +1,23 @@
"""UpdateService firmware inventory (``UpdateService/FirmwareInventory/*``)."""
from __future__ import annotations
from typing import Any, Mapping
from .common import boolean, link_list, status, text
def parse_firmware(item: Mapping[str, Any]) -> dict[str, Any]:
"""One SoftwareInventory member: what it is, its version, whether it can be updated."""
state, health = status(item)
return {
"id": text(item.get("Id")),
"name": text(item.get("Name")),
"version": text(item.get("Version")),
"updateable": boolean(item.get("Updateable")),
"manufacturer": text(item.get("Manufacturer")),
"release_date": text(item.get("ReleaseDate")),
"state": state,
"health": health,
"related": link_list(item, "RelatedItem"),
}
+128
View File
@@ -0,0 +1,128 @@
"""Hardware of the managed server: Processors, Memory, Storage, Drives, SimpleStorage."""
from __future__ import annotations
from typing import Any, Mapping
from .common import boolean, ident, integer, link, link_list, location_label, present, status, text
def parse_processor(cpu: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``Systems/{id}/Processors``."""
state, health = status(cpu)
return {
"id": text(cpu.get("Id")),
"name": text(cpu.get("Name")),
"socket": text(cpu.get("Socket")) or location_label(cpu),
"processor_type": text(cpu.get("ProcessorType")),
"manufacturer": ident(cpu.get("Manufacturer")),
"model": ident(cpu.get("Model")),
"architecture": text(cpu.get("ProcessorArchitecture")),
"instruction_set": text(cpu.get("InstructionSet")),
"max_speed_mhz": integer(cpu.get("MaxSpeedMHz")),
"cores": integer(cpu.get("TotalCores")),
"threads": integer(cpu.get("TotalThreads")),
"serial_number": ident(cpu.get("SerialNumber")),
"present": present(cpu),
"state": state,
"health": health,
}
def parse_memory(dimm: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``Systems/{id}/Memory``.
An enabled slot with a capacity of 0 counts as empty: some BMCs list empty slots so.
"""
state, health = status(dimm)
capacity = integer(dimm.get("CapacityMiB"))
return {
"id": text(dimm.get("Id")),
"name": text(dimm.get("Name")),
"location": (location_label(dimm) or text(dimm.get("DeviceLocator"))
or text(dimm.get("Name"))),
"capacity_mib": capacity,
"memory_device_type": text(dimm.get("MemoryDeviceType")),
"base_module_type": text(dimm.get("BaseModuleType")),
"speed_mhz": integer(dimm.get("OperatingSpeedMhz")),
"manufacturer": ident(dimm.get("Manufacturer")),
"part_number": ident(dimm.get("PartNumber")),
"serial_number": ident(dimm.get("SerialNumber")),
"present": present(dimm) and capacity != 0,
"state": state,
"health": health,
}
def _controller(ctrl: Mapping[str, Any]) -> dict[str, Any]:
state, health = status(ctrl)
return {
"name": text(ctrl.get("Name")),
"manufacturer": ident(ctrl.get("Manufacturer")),
"model": ident(ctrl.get("Model")),
"serial_number": ident(ctrl.get("SerialNumber")),
"firmware_version": text(ctrl.get("FirmwareVersion")),
"speed_gbps": ctrl.get("SpeedGbps") if isinstance(ctrl.get("SpeedGbps"), (int, float))
else None,
"state": state,
"health": health,
}
def parse_storage(storage: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``Systems/{id}/Storage``.
Drives are separate resources: ``drive_paths`` lists them for ``parse_drive``. Storage
1.15 and later move controllers into a collection at ``controllers_path``.
"""
state, health = status(storage)
controllers = storage.get("StorageControllers")
return {
"id": text(storage.get("Id")),
"name": text(storage.get("Name")),
"state": state,
"health": health,
"controllers": [_controller(c) for c in controllers if isinstance(c, Mapping)]
if isinstance(controllers, list) else [],
"controllers_path": link(storage, "Controllers"),
"drive_paths": link_list(storage, "Drives"),
}
def parse_drive(drive: Mapping[str, Any]) -> dict[str, Any]:
"""A Drive resource, as linked from Storage."""
state, health = status(drive)
return {
"id": text(drive.get("Id")),
"name": text(drive.get("Name")),
"manufacturer": ident(drive.get("Manufacturer")),
"model": ident(drive.get("Model")),
"serial_number": ident(drive.get("SerialNumber")),
"part_number": ident(drive.get("PartNumber")),
"revision": text(drive.get("Revision")),
"capacity_bytes": integer(drive.get("CapacityBytes")),
"media_type": text(drive.get("MediaType")),
"protocol": text(drive.get("Protocol")),
"failure_predicted": boolean(drive.get("FailurePredicted")),
"present": present(drive),
"state": state,
"health": health,
}
def parse_simple_storage(storage: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``Systems/{id}/SimpleStorage`` (older services, no Storage).
Its devices come out with the same keys as ``parse_drive``.
"""
state, health = status(storage)
devices = storage.get("Devices")
drives = [parse_drive(dev) for dev in devices if isinstance(dev, Mapping)] \
if isinstance(devices, list) else []
return {
"id": text(storage.get("Id")),
"name": text(storage.get("Name")),
"state": state,
"health": health,
"drives": drives,
}
+41
View File
@@ -0,0 +1,41 @@
"""LogServices and their entries (the SEL lives under Systems or Managers, by vendor)."""
from __future__ import annotations
from typing import Any, Mapping
from .common import boolean, integer, link, status, text
def parse_log_service(service: Mapping[str, Any]) -> dict[str, Any]:
"""One member of ``.../LogServices``."""
state, health = status(service)
return {
"id": text(service.get("Id")),
"name": text(service.get("Name")),
"enabled": boolean(service.get("ServiceEnabled")),
"entries_path": link(service, "Entries"),
"max_records": integer(service.get("MaxNumberOfRecords")),
"overwrite_policy": text(service.get("OverWritePolicy")),
"entry_type": text(service.get("LogEntryType")),
"state": state,
"health": health,
}
def parse_log_entry(entry: Mapping[str, Any]) -> dict[str, Any]:
"""One LogEntry (SEL record or Redfish event)."""
args = entry.get("MessageArgs")
return {
"id": text(entry.get("Id")),
"created": text(entry.get("Created")),
"severity": text(entry.get("Severity")),
"message": text(entry.get("Message")),
"message_id": text(entry.get("MessageId")),
"message_args": [str(a) for a in args] if isinstance(args, list) else [],
"entry_type": text(entry.get("EntryType")),
"entry_code": text(entry.get("EntryCode")),
"sensor_type": text(entry.get("SensorType")),
"sensor_number": integer(entry.get("SensorNumber")),
"origin": link(entry, "Links", "OriginOfCondition"),
}
+151
View File
@@ -0,0 +1,151 @@
"""Manager (the BMC itself), its EthernetInterfaces and its NetworkProtocol."""
from __future__ import annotations
import ipaddress
from typing import Any, Mapping
from .common import boolean, integer, link_list, links, number, status, text
def parse_manager(manager: Mapping[str, Any]) -> dict[str, Any]:
"""``Managers/{id}``: model, firmware and links of the BMC."""
state, health = status(manager)
return {
"id": text(manager.get("Id")),
"name": text(manager.get("Name")),
"manager_type": text(manager.get("ManagerType")),
"model": text(manager.get("Model")),
"firmware_version": text(manager.get("FirmwareVersion")),
"uuid": text(manager.get("UUID")),
"power_state": text(manager.get("PowerState")),
"date_time": text(manager.get("DateTime")),
"state": state,
"health": health,
"links": links(manager),
"manager_for_servers": link_list(manager, "Links", "ManagerForServers"),
"manager_for_chassis": link_list(manager, "Links", "ManagerForChassis"),
}
def _mac(value: Any) -> str:
mac = text(value)
return mac.upper().replace("-", ":") if mac else ""
def _ipv4(entries: Any) -> dict[str, dict[str, int]]:
found = {}
for entry in entries if isinstance(entries, list) else []:
if not isinstance(entry, Mapping):
continue
try:
addr = ipaddress.IPv4Address(text(entry.get("Address")) or "")
mask = text(entry.get("SubnetMask"))
prefix = ipaddress.IPv4Network(f"0.0.0.0/{mask}").prefixlen if mask else 32
except ValueError:
continue
if addr.is_unspecified:
continue
found[str(addr)] = {"prefix_length": prefix}
return found
def _ipv6(entries: Any) -> dict[str, dict[str, int]]:
found = {}
for entry in entries if isinstance(entries, list) else []:
if not isinstance(entry, Mapping):
continue
try:
addr = ipaddress.IPv6Address(text(entry.get("Address")) or "")
except ValueError:
continue
if addr.is_unspecified:
continue
prefix = integer(entry.get("PrefixLength"))
found[str(addr)] = {"prefix_length": prefix if prefix is not None else 128}
return found
def parse_ethernet_interface(iface: Mapping[str, Any]) -> dict[str, Any]:
"""One EthernetInterface, as NAPALM ``get_interfaces``/``get_interfaces_ip`` parts.
``interface`` is the ``get_interfaces`` value, ``ipv4``/``ipv6`` the ``get_interfaces_ip``
one. Without ``LinkStatus`` (older schemas) an enabled interface counts as up. Unset
addresses (``0.0.0.0``, ``::``) are left out.
"""
state, _ = status(iface)
enabled = boolean(iface.get("InterfaceEnabled"))
if enabled is None:
enabled = state == "Enabled"
link_status = text(iface.get("LinkStatus"))
is_up = enabled and (link_status == "LinkUp" if link_status else state == "Enabled")
speed = number(iface.get("SpeedMbps"))
vlan = iface.get("VLAN")
vlan_id = None
if isinstance(vlan, Mapping) and vlan.get("VLANEnable"):
vlan_id = integer(vlan.get("VLANId"))
return {
"name": text(iface.get("Id")),
"interface": {
"is_up": bool(is_up),
"is_enabled": bool(enabled),
"description": text(iface.get("Description")) or "",
"last_flapped": -1.0,
"speed": speed if speed is not None else -1.0,
"mtu": integer(iface.get("MTUSize")) or 0,
"mac_address": _mac(iface.get("MACAddress")),
},
"ipv4": _ipv4(iface.get("IPv4Addresses")),
"ipv6": _ipv6(iface.get("IPv6Addresses")),
"hostname": text(iface.get("HostName")),
"fqdn": text(iface.get("FQDN")),
"link_status": link_status,
"vlan": vlan_id,
"permanent_mac_address": _mac(iface.get("PermanentMACAddress")) or None,
}
def parse_network_protocol(proto: Mapping[str, Any]) -> dict[str, Any]:
"""``Managers/{id}/NetworkProtocol``: which services the BMC runs, NTP and SNMP.
``protocols`` holds every property with a ``ProtocolEnabled`` flag or a ``Port``, so
services the schema adds later show up without code changes. NTP servers and the SNMP details
exist only in newer schema versions; missing values are ``None``.
"""
protocols = {}
for key, value in proto.items():
if isinstance(value, Mapping) and ("ProtocolEnabled" in value or "Port" in value):
protocols[key] = {
"enabled": boolean(value.get("ProtocolEnabled")),
"port": integer(value.get("Port")),
}
ntp = proto.get("NTP") if isinstance(proto.get("NTP"), Mapping) else {}
servers: list[str] = []
for server in ntp.get("NTPServers") or []:
server = text(server)
if server and server not in servers:
servers.append(server)
snmp = proto.get("SNMP") if isinstance(proto.get("SNMP"), Mapping) else {}
communities = []
for community in snmp.get("CommunityStrings") or []:
if isinstance(community, Mapping):
# The community string itself is never kept, even if a BMC returns it.
communities.append({"name": text(community.get("Name")),
"access": text(community.get("AccessMode"))})
return {
"hostname": text(proto.get("HostName")),
"fqdn": text(proto.get("FQDN")),
"protocols": protocols,
"ntp": {"enabled": boolean(ntp.get("ProtocolEnabled")), "servers": servers},
"snmp": {
"enabled": boolean(snmp.get("ProtocolEnabled")),
"port": integer(snmp.get("Port")),
"v1": boolean(snmp.get("EnableSNMPv1")),
"v2c": boolean(snmp.get("EnableSNMPv2c")),
"v3": boolean(snmp.get("EnableSNMPv3")),
"communities": communities,
},
}
+29
View File
@@ -0,0 +1,29 @@
"""ServiceRoot: ``GET /redfish/v1/`` (readable without credentials)."""
from __future__ import annotations
from typing import Any, Mapping
from .common import link, links, text
def parse_service_root(root: Mapping[str, Any]) -> dict[str, Any]:
"""Version, identity and top-level links of a Redfish service.
``vendor`` and ``product`` exist from ServiceRoot 1.5 on; ``oem_vendors`` (the keys under
``Oem``) is what older services offer for picking a quirk module.
"""
found = links(root)
sessions = link(root, "Links", "Sessions")
if sessions is not None:
found["Sessions"] = sessions
oem = root.get("Oem")
return {
"name": text(root.get("Name")),
"redfish_version": text(root.get("RedfishVersion")),
"uuid": text(root.get("UUID")),
"vendor": text(root.get("Vendor")),
"product": text(root.get("Product")),
"oem_vendors": sorted(oem) if isinstance(oem, Mapping) else [],
"links": found,
}
+73
View File
@@ -0,0 +1,73 @@
"""ComputerSystem (``Systems/{id}``): the managed server, its power state and reset action."""
from __future__ import annotations
from typing import Any, Mapping
from .common import ident, integer, link_list, links, number, status, text
def parse_system(system: Mapping[str, Any]) -> dict[str, Any]:
"""Identity, summaries and links of the managed server."""
state, health = status(system)
cpus = system.get("ProcessorSummary") if isinstance(system.get("ProcessorSummary"),
Mapping) else {}
memory = system.get("MemorySummary") if isinstance(system.get("MemorySummary"),
Mapping) else {}
return {
"id": text(system.get("Id")),
"name": text(system.get("Name")),
"manufacturer": ident(system.get("Manufacturer")),
"model": ident(system.get("Model")),
"serial_number": ident(system.get("SerialNumber")),
"uuid": ident(system.get("UUID")),
"hostname": text(system.get("HostName")),
"sku": ident(system.get("SKU")),
"part_number": ident(system.get("PartNumber")),
"asset_tag": ident(system.get("AssetTag")),
"bios_version": text(system.get("BiosVersion")),
"system_type": text(system.get("SystemType")),
"power_state": parse_power_state(system),
"state": state,
"health": health,
"processor_count": integer(cpus.get("Count")),
"processor_model": text(cpus.get("Model")),
"memory_gib": number(memory.get("TotalSystemMemoryGiB")),
"links": links(system),
"chassis": link_list(system, "Links", "Chassis"),
"managed_by": link_list(system, "Links", "ManagedBy"),
}
def parse_power_state(system: Mapping[str, Any]) -> str | None:
"""``PowerState``: On, Off, PoweringOn, PoweringOff or Paused; ``None`` if not reported."""
return text(system.get("PowerState"))
def parse_reset_action(resource: Mapping[str, Any],
action: str = "#ComputerSystem.Reset") -> dict[str, Any]:
"""Target and allowed ``ResetType`` values of a reset action.
``allowed`` is ``None`` when the service does not list the values inline. Then
``action_info`` may name the ActionInfo resource that does (see ``parse_action_info``).
Works for ``#Manager.Reset`` too.
"""
actions = resource.get("Actions")
entry = actions.get(action) if isinstance(actions, Mapping) else None
if not isinstance(entry, Mapping):
return {"target": None, "allowed": None, "action_info": None}
allowed = entry.get("ResetType@Redfish.AllowableValues")
return {
"target": text(entry.get("target")),
"allowed": [str(v) for v in allowed] if isinstance(allowed, list) else None,
"action_info": text(entry.get("@Redfish.ActionInfo")),
}
def parse_action_info(info: Mapping[str, Any], parameter: str = "ResetType") -> list[str] | None:
"""``AllowableValues`` of one parameter in an ActionInfo resource."""
for param in info.get("Parameters") or []:
if isinstance(param, Mapping) and param.get("Name") == parameter:
values = param.get("AllowableValues")
return [str(v) for v in values] if isinstance(values, list) else None
return None
+327
View File
@@ -0,0 +1,327 @@
"""HTTPS transport to a Redfish service, on top of the DMTF ``redfish`` library.
The library does the HTTP work; this module adds what a BMC driver needs on top:
- Session login through the SessionService (``X-Auth-Token``), with the session deleted on
close, because BMCs allow only a few concurrent sessions. Basic auth is the fallback for
services that cannot create sessions, never a second try after a rejected password.
- Certificate verification that is on unless turned off per device. The library disables
verification whenever it is not given a CA file, so ``verify=True`` passes the CA bundle
of ``requests`` explicitly.
- Collections followed by ``@odata.id`` (including ``nextLink`` pages and expanded members)
instead of assumed member IDs.
- Links followed only on the BMC itself: an ``@odata.id`` pointing at another host (a PDU
outlet, for example) is refused, so the session token never leaves the BMC.
"""
from __future__ import annotations
import ipaddress
import logging
import os
from typing import Any, Mapping
from urllib.parse import urlsplit
import requests
from redfish.rest.v1 import (
AuthMethod,
InvalidCredentialsError,
RedfishPasswordChangeRequiredError,
RetriesExhaustedError,
ServerDownOrUnreachableError,
SessionCreationError,
redfish_client,
)
LOG = logging.getLogger(__name__)
SERVICE_ROOT = "/redfish/v1/"
DEFAULT_SESSIONS = "/redfish/v1/SessionService/Sessions"
AUTH_METHODS = ("auto", "session", "basic")
MAX_PAGES = 1000
Reference = str | Mapping[str, Any]
class RedfishError(Exception):
"""A Redfish request failed."""
def __init__(self, message: str, *, status: int | None = None, path: str | None = None):
super().__init__(message)
self.status = status
self.path = path
class RedfishAuthError(RedfishError):
"""Login failed, or the account may not read the resource (HTTP 401/403)."""
class RedfishHTTPError(RedfishError):
"""The service answered with an HTTP error status."""
class RedfishNotFound(RedfishHTTPError):
"""The resource does not exist (HTTP 404)."""
def _error_message(body: Any) -> str:
"""The human-readable part of a Redfish error body, if any."""
if not isinstance(body, Mapping):
return ""
error = body.get("error")
if not isinstance(error, Mapping):
return ""
messages = [
info.get("Message")
for info in error.get("@Message.ExtendedInfo") or []
if isinstance(info, Mapping) and info.get("Message")
]
if not messages and error.get("message"):
messages = [error["message"]]
return "; ".join(str(m) for m in messages)
class RedfishTransport:
"""One authenticated connection to a Redfish service.
:param hostname: BMC host name or IP address (IPv6 without brackets is fine).
:param port: HTTPS port, default 443.
:param verify: ``True`` verifies the certificate against the CA bundle of ``requests``,
a path verifies against that CA file, ``False`` turns verification off.
:param auth: ``"auto"`` (session, basic auth if the service cannot create sessions),
``"session"`` or ``"basic"``.
:param timeout: seconds per request.
:param retries: extra attempts after a connection error (the library waits 1 s between).
"""
def __init__(
self,
hostname: str,
username: str,
password: str,
*,
port: int | None = None,
verify: bool | str = True,
auth: str = "auto",
timeout: float = 30,
retries: int = 1,
):
if auth not in AUTH_METHODS:
raise ValueError(f"auth must be one of {', '.join(AUTH_METHODS)}, not {auth!r}")
if isinstance(verify, str) and not os.path.isfile(verify):
raise ValueError(f"CA file not found: {verify}")
self.hostname = hostname
self.username = username
self.password = password
self.port = port
self.verify = verify
self.auth = auth
self.timeout = timeout
self.retries = retries
self.root: dict[str, Any] = {}
self.auth_method: str | None = None
self._client = None
@property
def base_url(self) -> str:
host = self.hostname
try:
if ipaddress.ip_address(host).version == 6:
host = f"[{host}]"
except ValueError:
pass
return f"https://{host}" + (f":{self.port}" if self.port else "")
@property
def _cafile(self) -> str | None:
if self.verify is True:
return requests.certs.where()
if self.verify is False:
return None
return self.verify
# --- connection --------------------------------------------------------------------
def open(self) -> None:
"""Read the service root (no credentials needed) and log in. No-op when open."""
if self._client is not None:
return
try:
client = redfish_client(
base_url=self.base_url,
username=self.username,
password=self.password,
default_prefix=SERVICE_ROOT,
cafile=self._cafile,
timeout=self.timeout,
max_retry=self.retries,
)
except (RetriesExhaustedError, ServerDownOrUnreachableError) as exc:
raise RedfishError(f"{self.base_url}: service root not reachable: {exc}") from exc
except Exception as exc:
raise RedfishError(f"{self.base_url}: cannot read the service root: {exc}") from exc
self._client = client
self.root = dict(getattr(client, "root", None) or {})
try:
self.auth_method = self._login(client)
except BaseException:
self._client = None
client._session.close()
raise
def _login(self, client) -> str:
if self.auth == "basic":
self._call(client.login, auth=AuthMethod.BASIC)
return "basic"
try:
self._call(client.login, auth=AuthMethod.SESSION)
except _SessionUnsupported as exc:
if self.auth == "session":
raise RedfishAuthError(f"{self.base_url}: session login failed: {exc}") from exc
LOG.info("%s: no Redfish session (%s), falling back to basic auth", self.base_url, exc)
else:
if client.get_session_key():
return "session"
if self.auth == "session":
raise RedfishAuthError(f"{self.base_url}: session login returned no X-Auth-Token")
LOG.info("%s: session login returned no token, falling back to basic auth",
self.base_url)
self._call(client.login, auth=AuthMethod.BASIC)
return "basic"
def _call(self, login, **kwargs) -> None:
try:
login(**kwargs)
except InvalidCredentialsError as exc:
raise RedfishAuthError(f"{self.base_url}: invalid credentials", status=401) from exc
except RedfishPasswordChangeRequiredError as exc:
raise RedfishAuthError(f"{self.base_url}: the account must change its password "
"before it can be used") from exc
except SessionCreationError as exc:
raise _SessionUnsupported(str(exc).strip()) from exc
except (RetriesExhaustedError, ServerDownOrUnreachableError) as exc:
raise RedfishError(f"{self.base_url}: login failed: {exc}") from exc
def close(self) -> None:
"""Delete the session (if any) and close the HTTP connection. Never raises."""
client, self._client = self._client, None
self.auth_method = None
if client is None:
return
try:
client.logout()
except Exception as exc: # a leaked session times out; a failing close must not mask
LOG.warning("%s: could not delete the Redfish session: %s", self.base_url, exc)
client._session.close()
def __enter__(self) -> "RedfishTransport":
self.open()
return self
def __exit__(self, *exc_info) -> None:
self.close()
# --- requests ----------------------------------------------------------------------
def path(self, ref: Reference) -> str:
"""Request path for a path, an absolute URL on this BMC or an ``{"@odata.id": ...}``."""
if isinstance(ref, Mapping):
ref = ref.get("@odata.id")
if not isinstance(ref, str) or not ref:
raise RedfishError(f"not a Redfish reference: {ref!r}")
parts = urlsplit(ref)
if parts.scheme or parts.netloc:
ours = urlsplit(self.base_url)
if parts.scheme != "https" or parts.netloc.lower() != ours.netloc.lower():
raise RedfishError(f"refusing to follow a link to another host: {ref}")
if not parts.path.startswith("/"):
raise RedfishError(f"not an absolute Redfish path: {ref}")
return parts.path + (f"?{parts.query}" if parts.query else "")
def _request(self, method: str, ref: Reference, body: Any = None) -> dict[str, Any]:
if self._client is None:
raise RedfishError("not connected: call open() first")
path = self.path(ref)
try:
if method == "GET":
resp = self._client.get(path)
else:
resp = self._client.post(path, body=body)
except RetriesExhaustedError as exc:
raise RedfishError(f"{method} {path}: no response", path=path) from exc
status = resp.status
try:
data = resp.dict
except Exception as exc:
if status < 400:
raise RedfishError(f"{method} {path}: response is not JSON",
status=status, path=path) from exc
data = {}
if status < 400:
if not isinstance(data, dict):
raise RedfishError(f"{method} {path}: response is not a JSON object",
status=status, path=path)
return data
detail = _error_message(data)
message = f"{method} {path}: HTTP {status}" + (f": {detail}" if detail else "")
if status in (401, 403):
raise RedfishAuthError(message, status=status, path=path)
if status == 404:
raise RedfishNotFound(message, status=status, path=path)
raise RedfishHTTPError(message, status=status, path=path)
def get(self, ref: Reference) -> dict[str, Any]:
return self._request("GET", ref)
def get_optional(self, ref: Reference | None) -> dict[str, Any] | None:
"""GET, but ``None`` for a missing reference or a 404."""
if ref is None:
return None
try:
return self.get(ref)
except RedfishNotFound:
return None
def post(self, ref: Reference, body: Mapping[str, Any]) -> dict[str, Any]:
return self._request("POST", ref, dict(body))
def follow(self, resource: Mapping[str, Any], key: str) -> dict[str, Any] | None:
"""The resource linked as ``resource[key]``; ``None`` if not linked or not found."""
link = resource.get(key)
if not isinstance(link, Mapping) or "@odata.id" not in link:
return None
return self.get_optional(link)
def members(self, collection: Reference, *, limit: int | None = None) -> list[dict[str, Any]]:
"""All members of a collection, each fetched by its ``@odata.id``.
Members the service already expanded inline are used as they are. ``nextLink`` pages
are followed. ``limit`` stops after that many members.
"""
result: list[dict[str, Any]] = []
page_ref: Reference | None = collection
seen: set[str] = set()
while page_ref is not None and len(seen) < MAX_PAGES:
page_path = self.path(page_ref)
if page_path in seen:
LOG.warning("%s: nextLink loop at %s", self.base_url, page_path)
break
seen.add(page_path)
page = self.get(page_path)
for member in page.get("Members") or []:
if limit is not None and len(result) >= limit:
return result
if not isinstance(member, Mapping):
continue
if "Id" in member or "@odata.type" in member:
result.append(dict(member))
elif "@odata.id" in member:
result.append(self.get(member))
if limit is not None and len(result) >= limit:
break
page_ref = page.get("Members@odata.nextLink") or page.get("@odata.nextLink")
return result
class _SessionUnsupported(Exception):
"""Session creation failed for a reason other than bad credentials."""
+38
View File
@@ -0,0 +1,38 @@
[build-system]
requires = ["setuptools>=64"]
build-backend = "setuptools.build_meta"
[project]
name = "napalm-redfish"
version = "0.1.0"
description = "NAPALM driver for server baseboard management controllers over DMTF Redfish"
readme = "README.md"
license = { file = "LICENSE" }
requires-python = ">=3.10"
dependencies = [
"napalm>=5.0",
# Not on PyPI: install from git before this package (see README).
"napalm-device-types>=4.7",
"redfish>=3.2",
]
classifiers = [
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Topic :: System :: Hardware",
"Topic :: System :: Networking",
]
[project.optional-dependencies]
test = [
"pytest>=7",
"responses>=0.23",
]
[project.entry-points."napalm.drivers"]
redfish = "napalm_redfish:RedfishDriver"
[tool.setuptools.packages.find]
include = ["napalm_redfish*"]
[tool.pytest.ini_options]
testpaths = ["tests"]
+112
View File
@@ -0,0 +1,112 @@
"""Shared helpers for loading the recorded Redfish responses under tests/fixtures/."""
import json
import re
from pathlib import Path
from urllib.parse import urlsplit
import pytest
import responses
FIXTURES = Path(__file__).parent / "fixtures"
PREFIX = "/redfish/v1"
def fixture_file(mockup: str, path: str) -> Path:
"""Map a request path to its file: /redfish/v1/X/Y -> <mockup>/X/Y/index.json."""
path = path.split("#", 1)[0].split("?", 1)[0].rstrip("/")
if not path.startswith(PREFIX):
raise ValueError(f"not a Redfish path: {path}")
relative = path[len(PREFIX):].lstrip("/")
return FIXTURES / mockup / relative / "index.json"
def load(mockup: str, path: str) -> dict:
return json.loads(fixture_file(mockup, path).read_text())
@pytest.fixture
def rackmount():
"""Loader for the DMTF public-rackmount1 mockup."""
return lambda path: load("dmtf-public-rackmount1", path)
@pytest.fixture
def localstorage():
"""Loader for the DMTF public-localstorage mockup."""
return lambda path: load("dmtf-public-localstorage", path)
@pytest.fixture
def telemetry():
"""Loader for the DMTF public-telemetry mockup."""
return lambda path: load("dmtf-public-telemetry", path)
BASE_URL = "https://bmc.example.net"
SESSIONS = "/redfish/v1/SessionService/Sessions"
SESSION_PATH = SESSIONS + "/0123456789abcdef"
TOKEN = "secret-session-token"
class MockupServer:
"""Serve a fixture mockup over mocked HTTP, plus a SessionService that hands out one token.
Unknown paths return a Redfish error body with 404, like a real service. ``override``
replaces the answer for one method and path (the path may include a query string).
"""
def __init__(self, rsps, mockup: str):
self.rsps = rsps
self.mockup = mockup
self.overrides: dict[tuple[str, str], tuple[int, dict, dict]] = {}
anything = re.compile(re.escape(BASE_URL) + r"/.*")
for method in (responses.GET, responses.POST, responses.DELETE):
rsps.add_callback(method, anything, callback=self._handle)
def override(self, method: str, path: str, status: int, body: dict | str | None = None,
headers: dict | None = None):
self.overrides[(method, path)] = (status, body if body is not None else {}, headers or {})
@staticmethod
def _reply(status, body, headers=None):
if isinstance(body, str):
text = body
else:
text = json.dumps(body) if body != {} or status != 204 else ""
return status, {"Content-Type": "application/json", **(headers or {})}, text
def _handle(self, request):
parts = urlsplit(request.url)
for key in ((request.method, f"{parts.path}?{parts.query}"), (request.method, parts.path)):
if key in self.overrides:
return self._reply(*self.overrides[key])
if request.method == "POST" and parts.path == SESSIONS:
return self._reply(201, {"@odata.id": SESSION_PATH, "Id": "0123456789abcdef"},
{"X-Auth-Token": TOKEN, "Location": BASE_URL + SESSION_PATH})
if request.method == "DELETE" and parts.path == SESSION_PATH:
return self._reply(204, {})
if request.method == "GET":
try:
return self._reply(200, load(self.mockup, parts.path))
except (FileNotFoundError, ValueError):
pass
return self._reply(404, {"error": {"code": "Base.1.0.ResourceMissingAtURI",
"message": f"{parts.path} not found"}})
def calls(self, method: str | None = None, path: str | None = None):
return [c for c in self.rsps.calls
if (method is None or c.request.method == method)
and (path is None or urlsplit(c.request.url).path == path)]
@pytest.fixture
def http():
with responses.RequestsMock(assert_all_requests_are_fired=False) as rsps:
yield rsps
@pytest.fixture
def server(http):
"""The public-rackmount1 mockup served on BASE_URL."""
return MockupServer(http, "dmtf-public-rackmount1")
+17
View File
@@ -0,0 +1,17 @@
Source: https://github.com/DMTF/Redfish-Publications
Commit: 4f81814e399213c9055863ddaff42791c6b3706a
Mockup: mockups/public-localstorage
Files are unmodified copies. Each request path maps to <path below /redfish/v1>/index.json
(the service root is ./index.json), as in the mockup and as served by Redfish-Mockup-Server.
Request paths:
GET /redfish/v1/Chassis/1U
GET /redfish/v1/Chassis/1U/Thermal
GET /redfish/v1/Chassis/1U/Power
GET /redfish/v1/Systems/437XR1138R2/Storage
GET /redfish/v1/Systems/437XR1138R2/Storage/1
GET /redfish/v1/Chassis/1U/Drives/35D38F11ACEF7BD3
GET /redfish/v1/Chassis/1U/Drives/3F5A8C54207B7233
GET /redfish/v1/Chassis/1U/Drives/32ADF365C6C1B7BD
GET /redfish/v1/Chassis/1U/Drives/3D58ECBC375FD9F2
@@ -0,0 +1,40 @@
{
"@odata.type": "#Drive.v1_22_0.Drive",
"Id": "32ADF365C6C1B7BD",
"Name": "Drive Sample",
"IndicatorLED": "Lit",
"Model": "C123",
"Revision": "100A",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"CapacityBytes": 899527000064,
"FailurePredicted": false,
"Protocol": "SAS",
"MediaType": "HDD",
"Manufacturer": "Contoso",
"SerialNumber": "1234570",
"PartNumber": "C123-1111",
"Identifiers": [
{
"DurableNameFormat": "NAA",
"DurableName": "32ADF365C6C1B7BD"
}
],
"HotspareType": "Global",
"HotspareReplacementMode": "Revertible",
"EncryptionAbility": "SelfEncryptingDrive",
"EncryptionStatus": "Unlocked",
"RotationSpeedRPM": 15000,
"BlockSizeBytes": 512,
"CapableSpeedGbs": 12,
"NegotiatedSpeedGbs": 12,
"Actions": {
"#Drive.SecureErase": {
"target": "/redfish/v1/Chassis/1U/Drives/32ADF365C6C1B7BD/Actions/Drive.SecureErase"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Drives/32ADF365C6C1B7BD",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,46 @@
{
"@odata.type": "#Drive.v1_22_0.Drive",
"Id": "35D38F11ACEF7BD3",
"Name": "Drive Sample",
"IndicatorLED": "Lit",
"Model": "C123",
"Revision": "100A",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"CapacityBytes": 899527000064,
"FailurePredicted": false,
"Protocol": "SAS",
"MediaType": "HDD",
"Manufacturer": "Contoso",
"SerialNumber": "1234567",
"PartNumber": "C123-1111",
"Identifiers": [
{
"DurableNameFormat": "NAA",
"DurableName": "35D38F11ACEF7BD3"
}
],
"HotspareType": "None",
"EncryptionAbility": "SelfEncryptingDrive",
"EncryptionStatus": "Unlocked",
"RotationSpeedRPM": 15000,
"BlockSizeBytes": 512,
"CapableSpeedGbs": 12,
"NegotiatedSpeedGbs": 12,
"Links": {
"Volumes": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/1"
}
]
},
"Actions": {
"#Drive.SecureErase": {
"target": "/redfish/v1/Chassis/1U/Drives/35D38F11ACEF7BD3/Actions/Drive.SecureErase"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Drives/35D38F11ACEF7BD3",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,49 @@
{
"@odata.type": "#Drive.v1_22_0.Drive",
"Id": "3D58ECBC375FD9F2",
"Name": "Drive Sample",
"IndicatorLED": "Lit",
"Model": "C123",
"Revision": "100A",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"CapacityBytes": 899527000064,
"FailurePredicted": false,
"Protocol": "SAS",
"MediaType": "HDD",
"Manufacturer": "Contoso",
"SerialNumber": "1234568",
"PartNumber": "C123-1111",
"Identifiers": [
{
"DurableNameFormat": "NAA",
"DurableName": "32ADF365C6C1B7BD"
}
],
"HotspareType": "None",
"EncryptionAbility": "SelfEncryptingDrive",
"EncryptionStatus": "Unlocked",
"RotationSpeedRPM": 15000,
"BlockSizeBytes": 512,
"CapableSpeedGbs": 12,
"NegotiatedSpeedGbs": 12,
"Links": {
"Volumes": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/2"
},
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/3"
}
]
},
"Actions": {
"#Drive.SecureErase": {
"target": "/redfish/v1/Chassis/1U/Drives/3D58ECBC375FD9F2/Actions/Drive.SecureErase"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Drives/3D58ECBC375FD9F2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,46 @@
{
"@odata.type": "#Drive.v1_22_0.Drive",
"Id": "3F5A8C54207B7233",
"Name": "Drive Sample",
"IndicatorLED": "Lit",
"Model": "C123",
"Revision": "100A",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"CapacityBytes": 899527000064,
"FailurePredicted": false,
"Protocol": "SAS",
"MediaType": "HDD",
"Manufacturer": "Contoso",
"SerialNumber": "1234569",
"PartNumber": "C123-1111",
"Identifiers": [
{
"DurableNameFormat": "NAA",
"DurableName": "3F5A8C54207B7233"
}
],
"HotspareType": "None",
"EncryptionAbility": "SelfEncryptingDrive",
"EncryptionStatus": "Unlocked",
"RotationSpeedRPM": 15000,
"BlockSizeBytes": 512,
"CapableSpeedGbs": 12,
"NegotiatedSpeedGbs": 12,
"Links": {
"Volumes": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes/1"
}
]
},
"Actions": {
"#Drive.SecureErase": {
"target": "/redfish/v1/Chassis/1U/Drives/3F5A8C54207B7233/Actions/Drive.SecureErase"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Drives/3F5A8C54207B7233",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,139 @@
{
"@odata.type": "#Power.v1_7_3.Power",
"Id": "Power",
"Name": "Power",
"PowerControl": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/PowerControl/0",
"MemberId": "0",
"Name": "Server Power Control",
"PowerConsumedWatts": 344,
"PowerRequestedWatts": 800,
"PowerAvailableWatts": 0,
"PowerCapacityWatts": 800,
"PowerAllocatedWatts": 800,
"PowerMetrics": {
"IntervalInMin": 30,
"MinConsumedWatts": 271,
"MaxConsumedWatts": 489,
"AverageConsumedWatts": 319
},
"PowerLimit": {
"LimitInWatts": 500,
"LimitException": "LogEventOnly",
"CorrectionInMs": 50
},
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
],
"Status": {
"State": "Enabled",
"Health": "OK"
}
}
],
"Voltages": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/Voltages/0",
"MemberId": "0",
"Name": "VRM1 Voltage",
"SensorNumber": 11,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingVolts": 12,
"UpperThresholdNonCritical": 12.5,
"UpperThresholdCritical": 13,
"UpperThresholdFatal": 15,
"LowerThresholdNonCritical": 11.5,
"LowerThresholdCritical": 11,
"LowerThresholdFatal": 10,
"MinReadingRange": 0,
"MaxReadingRange": 20,
"PhysicalContext": "VoltageRegulator",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/Voltages/1",
"MemberId": "1",
"Name": "VRM2 Voltage",
"SensorNumber": 12,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingVolts": 5,
"UpperThresholdNonCritical": 5.5,
"UpperThresholdCritical": 7,
"LowerThresholdNonCritical": 4.75,
"LowerThresholdCritical": 4.5,
"MinReadingRange": 0,
"MaxReadingRange": 20,
"PhysicalContext": "VoltageRegulator",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
}
],
"PowerSupplies": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/PowerSupplies/0",
"MemberId": "0",
"Name": "Power Supply Bay",
"Status": {
"State": "Enabled",
"Health": "Warning"
},
"PowerSupplyType": "AC",
"LineInputVoltageType": "ACWideRange",
"LineInputVoltage": 120,
"PowerCapacityWatts": 800,
"LastPowerOutputWatts": 325,
"Model": "499253-B21",
"Manufacturer": "ManufacturerName",
"FirmwareVersion": "1.00",
"SerialNumber": "1Z0000001",
"PartNumber": "0000001A3A",
"SparePartNumber": "0000001A3A",
"InputRanges": [
{
"InputType": "AC",
"MinimumVoltage": 100,
"MaximumVoltage": 120,
"OutputWattage": 800
},
{
"InputType": "AC",
"MinimumVoltage": 200,
"MaximumVoltage": 240,
"OutputWattage": 1300
}
],
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Power",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,159 @@
{
"@odata.type": "#Thermal.v1_7_3.Thermal",
"Id": "Thermal",
"Name": "Thermal",
"Temperatures": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Temperatures/0",
"MemberId": "0",
"Name": "CPU1 Temp",
"SensorNumber": 5,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingCelsius": 41,
"UpperThresholdNonCritical": 42,
"UpperThresholdCritical": 45,
"UpperThresholdFatal": 48,
"MinReadingRangeTemp": 0,
"MaxReadingRangeTemp": 60,
"PhysicalContext": "CPU",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU1"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Temperatures/1",
"MemberId": "1",
"Name": "CPU2 Temp",
"SensorNumber": 6,
"Status": {
"State": "Disabled"
},
"UpperThresholdNonCritical": 42,
"UpperThresholdCritical": 45,
"UpperThresholdFatal": 48,
"MinReadingRangeTemp": 0,
"MaxReadingRangeTemp": 60,
"PhysicalContext": "CPU",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU2"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Temperatures/2",
"MemberId": "2",
"Name": "Chassis Intake Temp",
"SensorNumber": 9,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingCelsius": 25,
"UpperThresholdNonCritical": 30,
"UpperThresholdCritical": 40,
"UpperThresholdFatal": 50,
"LowerThresholdNonCritical": 10,
"LowerThresholdCritical": 5,
"LowerThresholdFatal": 0,
"MinReadingRangeTemp": 0,
"MaxReadingRangeTemp": 60,
"PhysicalContext": "Intake",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Chassis/1U"
},
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
}
]
}
],
"Fans": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/0",
"MemberId": "0",
"Name": "BaseBoard System Fan",
"PhysicalContext": "Backplane",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 2100,
"ReadingUnits": "RPM",
"LowerThresholdFatal": 0,
"MinReadingRange": 0,
"MaxReadingRange": 5000,
"Redundancy": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Redundancy/0"
}
],
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/1",
"MemberId": "1",
"Name": "BaseBoard System Fan Backup",
"PhysicalContext": "Backplane",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 2050,
"ReadingUnits": "RPM",
"LowerThresholdFatal": 0,
"MinReadingRange": 0,
"MaxReadingRange": 5000,
"Redundancy": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Redundancy/0"
}
],
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
}
],
"Redundancy": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Redundancy/0",
"MemberId": "0",
"Name": "BaseBoard System Fans",
"RedundancySet": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/0"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/1"
}
],
"Mode": "N+m",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"MinNumNeeded": 1,
"MaxNumSupported": 2
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Thermal",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,46 @@
{
"@odata.type": "#Chassis.v1_29_0.Chassis",
"Id": "1U",
"Name": "Computer System Chassis",
"ChassisType": "RackMount",
"AssetTag": "Chicago-45Z-2381",
"Manufacturer": "Contoso",
"Model": "3500RX",
"SKU": "8675309",
"SerialNumber": "437XR1138R2",
"PartNumber": "224071-J23",
"PowerState": "On",
"IndicatorLED": "Lit",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Thermal": {
"@odata.id": "/redfish/v1/Chassis/1U/Thermal"
},
"Power": {
"@odata.id": "/redfish/v1/Chassis/1U/Power"
},
"Drives": {
"@odata.id": "/redfish/v1/Chassis/1U/Drives"
},
"Links": {
"ComputerSystems": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
}
],
"ManagedBy": [
{
"@odata.id": "/redfish/v1/Managers/BMC"
}
],
"ManagersInChassis": [
{
"@odata.id": "/redfish/v1/Managers/BMC"
}
]
},
"@odata.id": "/redfish/v1/Chassis/1U",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
+29
View File
@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2014-2025, Contributing Member(s) of Distributed Management Task
Force, Inc.. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,73 @@
{
"@odata.type": "#Storage.v1_23_0.Storage",
"Id": "1",
"Name": "Local Storage Controller",
"Description": "Integrated RAID Controller",
"Status": {
"State": "Enabled",
"Health": "OK",
"HealthRollup": "OK"
},
"StorageControllers": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1#/StorageControllers/0",
"MemberId": "0",
"Name": "Contoso Integrated RAID",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Identifiers": [
{
"DurableNameFormat": "NAA",
"DurableName": "345C59DBD970859C"
}
],
"Manufacturer": "Contoso",
"Model": "12Gbs Integrated RAID",
"SerialNumber": "2M220100SL",
"PartNumber": "CT18754",
"SpeedGbps": 12,
"FirmwareVersion": "1.0.0.7",
"SupportedControllerProtocols": [
"PCIe"
],
"SupportedDeviceProtocols": [
"SAS",
"SATA"
],
"CacheSummary": {
"TotalCacheSizeMiB": 1024,
"PersistentCacheSizeMiB": 1024,
"Status": {
"State": "Enabled",
"Health": "OK"
}
}
}
],
"Drives": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Drives/35D38F11ACEF7BD3"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Drives/3F5A8C54207B7233"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Drives/32ADF365C6C1B7BD"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Drives/3D58ECBC375FD9F2"
}
],
"Volumes": {
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1/Volumes"
},
"Actions": {
"#Storage.SetEncryptionKey": {
"target": "/redfish/v1/Systems/437XR1138R2/Storage/1/Actions/Storage.SetEncryptionKey"
}
},
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,12 @@
{
"@odata.type": "#StorageCollection.StorageCollection",
"Name": "Storage Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage/1"
}
],
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Storage",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,33 @@
{
"@odata.type": "#ManagerAccount.v1_14_2.ManagerAccount",
"Id": "1",
"Name": "User Account",
"Description": "User Account",
"Enabled": true,
"Password": null,
"PasswordChangeRequired": false,
"AccountTypes": [
"Redfish"
],
"UserName": "Administrator",
"EmailAddress": "Tiffany@contoso.com",
"PhoneNumber": "312-555-1212",
"OneTimePasscodeDeliveryAddress": "3125551212@smsrelay.contoso.com",
"RoleId": "Administrator",
"Locked": false,
"Certificates": {
"@odata.id": "/redfish/v1/AccountService/Accounts/1/Certificates"
},
"Links": {
"Role": {
"@odata.id": "/redfish/v1/AccountService/Roles/Administrator"
}
},
"Actions": {
"#ManagerAccount.ChangePassword": {
"target": "/redfish/v1/AccountService/Accounts/1/Actions/ManagerAccount.ChangePassword"
}
},
"@odata.id": "/redfish/v1/AccountService/Accounts/1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,38 @@
{
"@odata.type": "#ManagerAccount.v1_14_2.ManagerAccount",
"Id": "2",
"Name": "Employee #457",
"Description": "This user is both a Redfish administrator and a SNMPv3 user.",
"Enabled": true,
"Password": null,
"UserName": "contoso_employee457",
"MFABypass": {
"BypassTypes": [
"All"
]
},
"RoleId": "Administrator",
"Locked": false,
"AccountTypes": [
"Redfish",
"SNMP"
],
"SNMP": {
"AuthenticationKey": null,
"AuthenticationProtocol": "None",
"EncryptionKey": null,
"EncryptionProtocol": "CFB128_AES128"
},
"Links": {
"Role": {
"@odata.id": "/redfish/v1/AccountService/Roles/Administrator"
}
},
"Actions": {
"#ManagerAccount.ChangePassword": {
"target": "/redfish/v1/AccountService/Accounts/2/Actions/ManagerAccount.ChangePassword"
}
},
"@odata.id": "/redfish/v1/AccountService/Accounts/2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,12 @@
{
"@odata.type": "#ManagerAccountCollection.ManagerAccountCollection",
"Name": "Accounts Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/AccountService/Accounts/1"
}
],
"@odata.id": "/redfish/v1/AccountService/Accounts",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,160 @@
{
"@odata.type": "#AccountService.v1_19_0.AccountService",
"Id": "AccountService",
"Name": "Account Service",
"Description": "Account Service",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ServiceEnabled": true,
"AuthFailureLoggingThreshold": 3,
"MinPasswordLength": 8,
"AccountLockoutThreshold": 5,
"AccountLockoutDuration": 30,
"AccountLockoutCounterResetAfter": 30,
"AccountLockoutCounterResetEnabled": true,
"EnforcePasswordHistoryCount": 10,
"Accounts": {
"@odata.id": "/redfish/v1/AccountService/Accounts"
},
"Roles": {
"@odata.id": "/redfish/v1/AccountService/Roles"
},
"LocalAccountAuth": "Enabled",
"MultiFactorAuth": {
"OneTimePasscode": {
"Enabled": true
}
},
"LDAP": {
"AccountProviderType": "LDAPService",
"ServiceEnabled": false,
"ServiceAddresses": [
"ldaps://ldap.example.org:636"
],
"Authentication": {
"AuthenticationType": "UsernameAndPassword",
"Username": "cn=Manager,dc=example,dc=org",
"Password": null
},
"LDAPService": {
"SearchSettings": {
"BaseDistinguishedNames": [
"dc=example,dc=org"
],
"UsernameAttribute": "uid",
"GroupsAttribute": "memberof",
"EmailAttribute": "mail"
}
},
"RemoteRoleMapping": [
{
"RemoteUser": "cn=Manager,dc=example,dc=org",
"LocalRole": "Administrator",
"LocalAccountTypes": [
"Redfish",
"HostConsole",
"ManagerConsole"
]
},
{
"RemoteGroup": "cn=Admins,ou=Groups,dc=example,dc=org",
"LocalRole": "Administrator",
"LocalAccountTypes": [
"Redfish",
"HostConsole",
"ManagerConsole"
]
},
{
"RemoteGroup": "cn=PowerUsers,ou=Groups,dc=example,dc=org",
"LocalRole": "Operator",
"LocalAccountTypes": [
"Redfish",
"HostConsole"
]
},
{
"RemoteGroup": "(cn=*)",
"LocalRole": "ReadOnly",
"LocalAccountTypes": [
"Redfish"
]
}
]
},
"ActiveDirectory": {
"AccountProviderType": "ActiveDirectoryService",
"ServiceEnabled": true,
"ServiceAddresses": [
"ad1.example.org",
"ad2.example.org",
null,
null
],
"Authentication": {
"AuthenticationType": "KerberosKeytab",
"KerberosKeytab": null
},
"RemoteRoleMapping": [
{
"RemoteGroup": "Administrators",
"LocalRole": "Administrator",
"LocalAccountTypes": [
"Redfish",
"HostConsole",
"ManagerConsole"
]
},
{
"RemoteUser": "DOMAIN\\Bob",
"LocalRole": "Operator",
"LocalAccountTypes": [
"Redfish",
"HostConsole"
]
},
{
"RemoteGroup": "PowerUsers",
"LocalRole": "Operator",
"LocalAccountTypes": [
"Redfish",
"HostConsole"
]
},
{
"RemoteGroup": "Everybody",
"LocalRole": "ReadOnly",
"LocalAccountTypes": [
"Redfish"
]
}
]
},
"OAuth2": {
"AccountProviderType": "OAuth2",
"ServiceEnabled": true,
"ServiceAddresses": [
"https://contoso.org/.well-known/oauth-authorization-server"
],
"OAuth2Service": {
"Mode": "Discovery",
"Issuer": "https://contoso.org/services/oauth2",
"Audience": [
"23:11:8A:33:CF:EA",
"92384634-2938-2342-8820-489239905423"
],
"OAuthServiceSigningKeys": "ewogICAgImtleXMiOiBbCiAgICAgICAgewogICAgICAgICAgICAiZSI6ICJBUUFCIiwKICAgICAgICAgICAgImtpZCI6ICJvV1F2Tl8tRUxnd25HMzFNTEl4Rm5hTVlfNTd0Wk9FSUVQYnpaYlBqS2FBIiwKICAgICAgICAgICAgImt0eSI6ICJSU0EiLAogICAgICAgICAgICAibiI6ICJvYS1Kb2pkSGw2VHQ3N2Vma2h6aW5MMU9XNmZjMGhjQjloOTN2WUZyRVlPakozRjhfTFh1c1ItMW95MUFKdExZcF9KeVI4Q1ZET28yVGNCaTBuSkhKczF1dkN4WWljX202WE5vMHptVm1VQ2hVMnBYSlBGelZKRGV5VVhIWWVONG5yQ1k4XzlTLXlzWnlKa2RRaWJmeHlBRndUMHdpNlZldF9MYTE4N3JNZUFCU0tXdEdIVmpkOWk1UmhELWNGU1JaNk05M0s2bXFPUUpaX0pMXzJKUU93aGk5dFl4Q2drVVpqc1gyWXowMEZpdUVTWDhHcXRGQlI1UzhIT2p0b2FhZUJrTk5hYWZFOFdSUVl6QlVmOHpiNkVobUFMemc0Z0YxTVhRb3dZV25GcGpzYXcwNVRONHk3SWZJUVhLODlqbGxnbWFkWVVjbndaUmRMc1o2VTdBS1EiCiAgICAgICAgfQogICAgXQp9"
}
},
"AdditionalExternalAccountProviders": {
"@odata.id": "/redfish/v1/AccountService/ExternalAccountProviders"
},
"OutboundConnections": {
"@odata.id": "/redfish/v1/AccountService/OutboundConnections"
},
"RequireChangePasswordAction": false,
"@odata.id": "/redfish/v1/AccountService",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
+109
View File
@@ -0,0 +1,109 @@
Source: https://github.com/DMTF/Redfish-Publications
Commit: 4f81814e399213c9055863ddaff42791c6b3706a
Mockup: mockups/public-rackmount1
Files are unmodified copies. Each request path maps to <path below /redfish/v1>/index.json
(the service root is ./index.json), as in the mockup and as served by Redfish-Mockup-Server.
Request paths:
GET /redfish/v1/
GET /redfish/v1/AccountService
GET /redfish/v1/AccountService/Accounts
GET /redfish/v1/AccountService/Accounts/1
GET /redfish/v1/AccountService/Accounts/2
GET /redfish/v1/Chassis
GET /redfish/v1/Chassis/1U
GET /redfish/v1/Chassis/1U/Power
GET /redfish/v1/Chassis/1U/PowerSubsystem
GET /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies
GET /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1
GET /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Metrics
GET /redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay2
GET /redfish/v1/Chassis/1U/Sensors
GET /redfish/v1/Chassis/1U/Sensors/AmbientTemp
GET /redfish/v1/Chassis/1U/Sensors/Battery1InputCurrent
GET /redfish/v1/Chassis/1U/Sensors/Battery1InputVoltage
GET /redfish/v1/Chassis/1U/Sensors/Battery1OutputCurrent
GET /redfish/v1/Chassis/1U/Sensors/Battery1OutputVoltage
GET /redfish/v1/Chassis/1U/Sensors/Battery1StateOfHealth
GET /redfish/v1/Chassis/1U/Sensors/Battery1Temp
GET /redfish/v1/Chassis/1U/Sensors/CPU1Power
GET /redfish/v1/Chassis/1U/Sensors/CPU1Temp
GET /redfish/v1/Chassis/1U/Sensors/CPUFan1
GET /redfish/v1/Chassis/1U/Sensors/CPUFan2
GET /redfish/v1/Chassis/1U/Sensors/DIMM1Temp
GET /redfish/v1/Chassis/1U/Sensors/DIMM2Temp
GET /redfish/v1/Chassis/1U/Sensors/DIMM3Temp
GET /redfish/v1/Chassis/1U/Sensors/ExhaustTemp
GET /redfish/v1/Chassis/1U/Sensors/FanBay1
GET /redfish/v1/Chassis/1U/Sensors/FanBay2
GET /redfish/v1/Chassis/1U/Sensors/IntakeTemp
GET /redfish/v1/Chassis/1U/Sensors/PS1Energy
GET /redfish/v1/Chassis/1U/Sensors/PS1Frequency
GET /redfish/v1/Chassis/1U/Sensors/PS1InputCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS1InputPower
GET /redfish/v1/Chassis/1U/Sensors/PS1InputVoltage
GET /redfish/v1/Chassis/1U/Sensors/PS1Out12V
GET /redfish/v1/Chassis/1U/Sensors/PS1Out12VCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS1Out3V
GET /redfish/v1/Chassis/1U/Sensors/PS1Out3VCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS1Out5V
GET /redfish/v1/Chassis/1U/Sensors/PS1Out5VCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS2Energy
GET /redfish/v1/Chassis/1U/Sensors/PS2Frequency
GET /redfish/v1/Chassis/1U/Sensors/PS2InputCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS2InputPower
GET /redfish/v1/Chassis/1U/Sensors/PS2InputVoltage
GET /redfish/v1/Chassis/1U/Sensors/PS2Out12V
GET /redfish/v1/Chassis/1U/Sensors/PS2Out12VCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS2Out3V
GET /redfish/v1/Chassis/1U/Sensors/PS2Out3VCurrent
GET /redfish/v1/Chassis/1U/Sensors/PS2Out5V
GET /redfish/v1/Chassis/1U/Sensors/PS2Out5VCurrent
GET /redfish/v1/Chassis/1U/Sensors/TotalEnergy
GET /redfish/v1/Chassis/1U/Sensors/TotalPower
GET /redfish/v1/Chassis/1U/Thermal
GET /redfish/v1/Chassis/1U/ThermalSubsystem
GET /redfish/v1/Chassis/1U/ThermalSubsystem/Fans
GET /redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay1
GET /redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay2
GET /redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU1
GET /redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU2
GET /redfish/v1/Chassis/1U/ThermalSubsystem/ThermalMetrics
GET /redfish/v1/Managers
GET /redfish/v1/Managers/BMC
GET /redfish/v1/Managers/BMC/EthernetInterfaces
GET /redfish/v1/Managers/BMC/EthernetInterfaces/ToHost
GET /redfish/v1/Managers/BMC/EthernetInterfaces/eth0
GET /redfish/v1/Managers/BMC/LogServices
GET /redfish/v1/Managers/BMC/LogServices/Log
GET /redfish/v1/Managers/BMC/LogServices/Log/Entries
GET /redfish/v1/Managers/BMC/NetworkProtocol
GET /redfish/v1/SessionService
GET /redfish/v1/SessionService/Sessions
GET /redfish/v1/Systems
GET /redfish/v1/Systems/437XR1138R2
GET /redfish/v1/Systems/437XR1138R2/EthernetInterfaces
GET /redfish/v1/Systems/437XR1138R2/EthernetInterfaces/12446A3B0411
GET /redfish/v1/Systems/437XR1138R2/EthernetInterfaces/12446A3B8890
GET /redfish/v1/Systems/437XR1138R2/EthernetInterfaces/ToManager
GET /redfish/v1/Systems/437XR1138R2/EthernetInterfaces/VLAN1
GET /redfish/v1/Systems/437XR1138R2/LogServices
GET /redfish/v1/Systems/437XR1138R2/LogServices/Log1
GET /redfish/v1/Systems/437XR1138R2/LogServices/Log1/Entries
GET /redfish/v1/Systems/437XR1138R2/Memory
GET /redfish/v1/Systems/437XR1138R2/Memory/DIMM1
GET /redfish/v1/Systems/437XR1138R2/Memory/DIMM2
GET /redfish/v1/Systems/437XR1138R2/Memory/DIMM3
GET /redfish/v1/Systems/437XR1138R2/Memory/DIMM4
GET /redfish/v1/Systems/437XR1138R2/Processors
GET /redfish/v1/Systems/437XR1138R2/Processors/CPU1
GET /redfish/v1/Systems/437XR1138R2/Processors/CPU2
GET /redfish/v1/Systems/437XR1138R2/Processors/FPGA1
GET /redfish/v1/Systems/437XR1138R2/SimpleStorage
GET /redfish/v1/Systems/437XR1138R2/SimpleStorage/1
GET /redfish/v1/UpdateService
GET /redfish/v1/UpdateService/FirmwareInventory
GET /redfish/v1/UpdateService/FirmwareInventory/BIOS
GET /redfish/v1/UpdateService/FirmwareInventory/BMC
GET /redfish/v1/UpdateService/FirmwareInventory/SS
@@ -0,0 +1,139 @@
{
"@odata.type": "#Power.v1_7_3.Power",
"Id": "Power",
"Name": "Power",
"PowerControl": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/PowerControl/0",
"MemberId": "0",
"Name": "System Input Power",
"PowerConsumedWatts": 344,
"PowerRequestedWatts": 800,
"PowerAvailableWatts": 0,
"PowerCapacityWatts": 800,
"PowerAllocatedWatts": 800,
"PowerMetrics": {
"IntervalInMin": 30,
"MinConsumedWatts": 271,
"MaxConsumedWatts": 489,
"AverageConsumedWatts": 319
},
"PowerLimit": {
"LimitInWatts": 500,
"LimitException": "LogEventOnly",
"CorrectionInMs": 50
},
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
],
"Status": {
"State": "Enabled",
"Health": "OK"
}
}
],
"Voltages": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/Voltages/0",
"MemberId": "0",
"Name": "VRM1 Voltage",
"SensorNumber": 11,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingVolts": 12,
"UpperThresholdNonCritical": 12.5,
"UpperThresholdCritical": 13,
"UpperThresholdFatal": 15,
"LowerThresholdNonCritical": 11.5,
"LowerThresholdCritical": 11,
"LowerThresholdFatal": 10,
"MinReadingRange": 0,
"MaxReadingRange": 20,
"PhysicalContext": "VoltageRegulator",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/Voltages/1",
"MemberId": "1",
"Name": "VRM2 Voltage",
"SensorNumber": 12,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingVolts": 5,
"UpperThresholdNonCritical": 5.5,
"UpperThresholdCritical": 7,
"LowerThresholdNonCritical": 4.75,
"LowerThresholdCritical": 4.5,
"MinReadingRange": 0,
"MaxReadingRange": 20,
"PhysicalContext": "VoltageRegulator",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
}
],
"PowerSupplies": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Power#/PowerSupplies/0",
"MemberId": "0",
"Name": "Power Supply Bay",
"Status": {
"State": "Enabled",
"Health": "Warning"
},
"PowerSupplyType": "AC",
"LineInputVoltageType": "ACWideRange",
"LineInputVoltage": 120,
"PowerCapacityWatts": 800,
"LastPowerOutputWatts": 325,
"Model": "499253-B21",
"Manufacturer": "ManufacturerName",
"FirmwareVersion": "1.00",
"SerialNumber": "1Z0000001",
"PartNumber": "0000001A3A",
"SparePartNumber": "0000001A3A",
"InputRanges": [
{
"InputType": "AC",
"MinimumVoltage": 100,
"MaximumVoltage": 120,
"OutputWattage": 800
},
{
"InputType": "AC",
"MinimumVoltage": 200,
"MaximumVoltage": 240,
"OutputWattage": 1300
}
],
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Power",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,85 @@
{
"@odata.type": "#PowerSupplyMetrics.v1_3_0.PowerSupplyMetrics",
"Id": "Metrics",
"Name": "Metrics for Power Supply 1",
"Status": {
"State": "Enabled",
"Health": "Warning"
},
"InputVoltage": {
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1InputVoltage",
"Reading": 230.2
},
"InputCurrentAmps": {
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1InputCurrent",
"Reading": 5.19
},
"InputPowerWatts": {
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1InputPower",
"Reading": 937.4
},
"RailVoltage": [
{
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Out3V",
"Reading": 3.31
},
{
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Out5V",
"Reading": 5.03
},
{
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Out12V",
"Reading": 12.06
}
],
"RailCurrentAmps": [
{
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Out3VCurrent",
"Reading": 9.84
},
{
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Out5VCurrent",
"Reading": 1.25
},
{
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Out12VCurrent",
"Reading": 2.58
}
],
"OutputPowerWatts": {
"Reading": 937.4
},
"RailPowerWatts": [
{
"Reading": 79.84
},
{
"Reading": 26.25
},
{
"Reading": 91.58
}
],
"EnergykWh": {
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Energy",
"Reading": 325675
},
"FrequencyHz": {
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/PS1Frequency",
"Reading": 60
},
"TemperatureCelsius": {
"Reading": 43.9
},
"FanSpeedPercent": {
"Reading": 68,
"SpeedRPM": 3290
},
"Actions": {
"#PowerSupplyMetrics.ResetMetrics": {
"target": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Metrics/Actions/PowerSupplyMetrics.ResetMetrics"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Metrics",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,99 @@
{
"@odata.type": "#PowerSupply.v1_8_0.PowerSupply",
"Id": "Bay1",
"Name": "Power Supply Bay 1",
"Status": {
"State": "Enabled",
"Health": "Warning",
"Conditions": [
{
"MessageId": "Power.1.0.PowerSupplyPredictiveFailure",
"Timestamp": "2023-06-08T12:25:00-05:00",
"Message": "Power supply 'PSU 1' has a predicted failure condition.",
"Severity": "Warning",
"MessageArgs": [
"PSU 1"
]
}
]
},
"LineInputStatus": "Normal",
"Model": "RKS-440DC",
"Manufacturer": "Contoso Power",
"FirmwareVersion": "1.00",
"SerialNumber": "3488247",
"PartNumber": "23456-133",
"SparePartNumber": "93284-133",
"LocationIndicatorActive": false,
"HotPluggable": false,
"PowerCapacityWatts": 400,
"PhaseWiringType": "OnePhase3Wire",
"PlugType": "IEC_60320_C14",
"InputRanges": [
{
"NominalVoltageType": "AC200To240V",
"CapacityWatts": 400
},
{
"NominalVoltageType": "AC120V",
"CapacityWatts": 350
},
{
"NominalVoltageType": "DC380V",
"CapacityWatts": 400
}
],
"EfficiencyRatings": [
{
"LoadPercent": 25,
"EfficiencyPercent": 75
},
{
"LoadPercent": 50,
"EfficiencyPercent": 85
},
{
"LoadPercent": 90,
"EfficiencyPercent": 80
}
],
"OutputRails": [
{
"NominalVoltage": 3.3,
"PhysicalContext": "SystemBoard"
},
{
"NominalVoltage": 5,
"PhysicalContext": "SystemBoard"
},
{
"NominalVoltage": 12,
"PhysicalContext": "StorageDevice"
}
],
"Location": {
"PartLocation": {
"ServiceLabel": "PSU 1",
"LocationType": "Bay",
"LocationOrdinalValue": 0
}
},
"Links": {
"Outlet": {
"@odata.id": "https://redfishpdu.contoso.com/redfish/v1/PowerEquipment/RackPDUs/1/Outlets/A4"
}
},
"Assembly": {
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Assembly"
},
"Metrics": {
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Metrics"
},
"Actions": {
"#PowerSupply.Reset": {
"target": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1/Actions/PowerSupply.Reset"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,19 @@
{
"@odata.type": "#PowerSupply.v1_8_0.PowerSupply",
"Id": "Bay2",
"Name": "Power Supply Bay 2",
"Status": {
"State": "Absent"
},
"PowerSupplyType": "PSU",
"HotPluggable": false,
"Location": {
"PartLocation": {
"ServiceLabel": "PSU 2",
"LocationType": "Bay",
"LocationOrdinalValue": 1
}
},
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,15 @@
{
"@odata.type": "#PowerSupplyCollection.PowerSupplyCollection",
"Name": "Power Supply Collection",
"Members@odata.count": 2,
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay2"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,47 @@
{
"@odata.type": "#PowerSubsystem.v1_2_0.PowerSubsystem",
"Id": "PowerSubsystem",
"Name": "Power Subsystem for Chassis",
"CapacityWatts": 2000,
"Allocation": {
"RequestedWatts": 1500,
"AllocatedWatts": 1200
},
"PowerSupplyRedundancy": [
{
"RedundancyType": "Failover",
"MaxSupportedInGroup": 2,
"MinNeededInGroup": 1,
"MinNeededForFaultTolerance": 2,
"RedundancyGroup": [
{
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay2"
}
],
"ActiveRedundancyGroup": [
{
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies/Bay1"
}
],
"Status": {
"State": "UnavailableOffline",
"Health": "OK"
}
}
],
"PowerSupplies": {
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/PowerSupplies"
},
"Batteries": {
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem/Batteries"
},
"Status": {
"State": "Enabled",
"Health": "OK"
},
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,16 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "AmbientTemp",
"Name": "Ambient Temperature",
"PhysicalContext": "Room",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 22.5,
"ReadingUnits": "Cel",
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/AmbientTemp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,30 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "Battery1InputCurrent",
"Name": "Battery #1 Input Current",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingType": "Current",
"Reading": 0,
"ReadingUnits": "A",
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"PhysicalContext": "Battery",
"PhysicalSubContext": "Input",
"Thresholds": {
"UpperCritical": {
"Reading": 60,
"Activation": "Increasing",
"DwellTime": "PT1S"
},
"UpperCaution": {
"Reading": 55,
"Activation": "Increasing",
"DwellTime": "PT1S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1InputCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,30 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "Battery1InputVoltage",
"Name": "Battery #1 Input Voltage",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingType": "Voltage",
"Reading": 12.22,
"ReadingUnits": "V",
"ReadingRangeMin": 0,
"ReadingRangeMax": 15,
"PhysicalContext": "Battery",
"PhysicalSubContext": "Input",
"Thresholds": {
"UpperCritical": {
"Reading": 14,
"Activation": "Increasing",
"DwellTime": "PT1S"
},
"UpperCaution": {
"Reading": 13,
"Activation": "Increasing",
"DwellTime": "PT1S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1InputVoltage",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,30 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "Battery1OutputCurrent",
"Name": "Battery #1 Output Current",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingType": "Current",
"Reading": 0,
"ReadingUnits": "A",
"ReadingRangeMin": 0,
"ReadingRangeMax": 15,
"PhysicalContext": "Battery",
"PhysicalSubContext": "Output",
"Thresholds": {
"UpperCritical": {
"Reading": 10,
"Activation": "Increasing",
"DwellTime": "PT1S"
},
"UpperCaution": {
"Reading": 8,
"Activation": "Increasing",
"DwellTime": "PT1S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1OutputCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,30 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "Battery1OutputVoltage",
"Name": "Battery #1 Output Voltage",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingType": "Voltage",
"Reading": 12.22,
"ReadingUnits": "V",
"ReadingRangeMin": 0,
"ReadingRangeMax": 15,
"PhysicalContext": "Battery",
"PhysicalSubContext": "Output",
"Thresholds": {
"UpperCritical": {
"Reading": 14,
"Activation": "Increasing",
"DwellTime": "PT1S"
},
"UpperCaution": {
"Reading": 13,
"Activation": "Increasing",
"DwellTime": "PT1S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1OutputVoltage",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,24 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "Battery1StateOfHealth",
"Name": "Battery #1 State of Health",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingType": "Percent",
"Reading": 91,
"ReadingUnits": "%",
"ReadingRangeMin": 0,
"ReadingRangeMax": 100,
"PhysicalContext": "Battery",
"Thresholds": {
"LowerCritical": {
"Reading": 30,
"Activation": "Decreasing",
"DwellTime": "PT1S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1StateOfHealth",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,29 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "Battery1Temp",
"Name": "Battery #1 Temperature",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingType": "Temperature",
"Reading": 33,
"ReadingUnits": "Cel",
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"PhysicalContext": "Battery",
"Thresholds": {
"UpperCritical": {
"Reading": 60,
"DwellTime": "PT15S",
"Activation": "Increasing"
},
"UpperCaution": {
"Reading": 50,
"Activation": "Increasing",
"DwellTime": "PT15S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1Temp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,33 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "CPU1Power",
"Name": "Power reading for CPU #1",
"ReadingType": "Power",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 90,
"ReadingUnits": "W",
"Implementation": "Reported",
"ReadingTime": "2019-08-13T04:14:33+06:00",
"ReadingRangeMin": 0,
"ReadingRangeMax": 150,
"Accuracy": 0.2,
"Precision": 1,
"SensingInterval": "PT0.01S",
"PhysicalContext": "CPU",
"Thresholds": {
"UpperCriticalUser": {
"Reading": 115,
"Activation": "Increasing",
"DwellTime": "PT0.03S"
},
"UpperCautionUser": {
"Reading": 82,
"DwellTime": "PT1S"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPU1Power",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,83 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "CPU1Temp",
"Name": "CPU #1 Temperature",
"PhysicalContext": "CPU",
"Status": {
"State": "Enabled",
"Health": "Warning",
"Conditions": [
{
"MessageId": "Sensor.1.0.ReadingAboveUpperCautionThreshold",
"Timestamp": "2024-08-12T12:25:00-05:00",
"Message": "Sensor 'CPU1 Temp' reading of 44 (Cel) is above the 42 upper caution threshold.",
"Severity": "Warning",
"MessageArgs": [
"CPU1 Temp",
"44",
"Cel",
"42"
]
}
]
},
"Reading": 44,
"ReadingUnits": "Cel",
"Thresholds": {
"UpperCaution": {
"Reading": 42,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCautionUser": {
"Reading": 40,
"DwellTime": "PT5M",
"HysteresisReading": 0,
"HysteresisDuration": "PT10M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperCritical": {
"Reading": 45,
"DwellTime": "PT1M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCriticalUser": {
"Reading": 45,
"DwellTime": "PT10S",
"HysteresisReading": -1,
"HysteresisDuration": "PT5M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperFatal": {
"Reading": 50,
"DwellTime": "PT30S",
"Activation": "Increasing",
"@Redfish.WriteableProperties": []
}
},
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU1"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPU1Temp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,36 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "CPUFan1",
"Name": "CPU #1 Fan Speed",
"PhysicalContext": "CPU",
"Reading": 45,
"SpeedRPM": 1490,
"ReadingRangeMax": 100,
"ReadingRangeMin": 0,
"ReadingType": "Percent",
"ReadingUnits": "%",
"Status": {
"Health": "OK",
"State": "Enabled"
},
"Thresholds": {
"LowerCaution": {
"Reading": 5,
"DwellTime": "PT30S",
"Activation": "Decreasing",
"HysteresisReading": 2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"LowerCritical": {
"Reading": 0,
"DwellTime": "PT10S",
"Activation": "Decreasing",
"HysteresisReading": 2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPUFan1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,36 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "CPUFan2",
"Name": "CPU #2 Fan Speed",
"PhysicalContext": "CPU",
"Reading": 45,
"SpeedRPM": 1490,
"ReadingRangeMax": 100,
"ReadingRangeMin": 0,
"ReadingType": "Percent",
"ReadingUnits": "%",
"Status": {
"Health": "OK",
"State": "Enabled"
},
"Thresholds": {
"LowerCaution": {
"Reading": 5,
"DwellTime": "PT30S",
"Activation": "Decreasing",
"HysteresisReading": 2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"LowerCritical": {
"Reading": 0,
"DwellTime": "PT10S",
"Activation": "Decreasing",
"HysteresisReading": 2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPUFan2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,71 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "DIMM1Temp",
"Name": "DIMM #1 Temperature",
"PhysicalContext": "Memory",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 44,
"ReadingUnits": "Cel",
"Thresholds": {
"UpperCaution": {
"Reading": 55,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCautionUser": {
"Reading": 50,
"DwellTime": "PT5M",
"HysteresisReading": 0,
"HysteresisDuration": "PT10M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperCritical": {
"Reading": 65,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCriticalUser": {
"Reading": 60,
"DwellTime": "PT5M",
"HysteresisReading": -1,
"HysteresisDuration": "PT5M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperFatal": {
"Reading": 75,
"DwellTime": "PT30S",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT30S",
"@Redfish.WriteableProperties": []
}
},
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Memory/DIMM1"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/DIMM1Temp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,71 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "DIMM2Temp",
"Name": "DIMM #2 Temperature",
"PhysicalContext": "Memory",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 43,
"ReadingUnits": "Cel",
"Thresholds": {
"UpperCaution": {
"Reading": 55,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCautionUser": {
"Reading": 50,
"DwellTime": "PT5M",
"HysteresisReading": 0,
"HysteresisDuration": "PT10M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperCritical": {
"Reading": 65,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCriticalUser": {
"Reading": 60,
"DwellTime": "PT5M",
"HysteresisReading": -1,
"HysteresisDuration": "PT5M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperFatal": {
"Reading": 75,
"DwellTime": "PT30S",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT30S",
"@Redfish.WriteableProperties": []
}
},
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Memory/DIMM2"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/DIMM2Temp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,71 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "DIMM3Temp",
"Name": "DIMM #3 Temperature",
"PhysicalContext": "Memory",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 45,
"ReadingUnits": "Cel",
"Thresholds": {
"UpperCaution": {
"Reading": 55,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCautionUser": {
"Reading": 50,
"DwellTime": "PT5M",
"HysteresisReading": 0,
"HysteresisDuration": "PT10M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperCritical": {
"Reading": 65,
"DwellTime": "PT5M",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": []
},
"UpperCriticalUser": {
"Reading": 60,
"DwellTime": "PT5M",
"HysteresisReading": -1,
"HysteresisDuration": "PT5M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperFatal": {
"Reading": 75,
"DwellTime": "PT30S",
"Activation": "Increasing",
"HysteresisReading": -2,
"HysteresisDuration": "PT30S",
"@Redfish.WriteableProperties": []
}
},
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Memory/DIMM3"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/DIMM3Temp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,16 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "ExhaustTemp",
"Name": "Fan Bay #1 Exhaust Temperature",
"PhysicalContext": "Exhaust",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 40.5,
"ReadingUnits": "Cel",
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/ExhaustTemp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,26 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "FanBay1",
"Name": "Chassis Fan #1",
"PhysicalContext": "Chassis",
"Reading": 45,
"SpeedRPM": 1900,
"ReadingRangeMax": 100,
"ReadingRangeMin": 0,
"ReadingType": "Percent",
"ReadingUnits": "%",
"Status": {
"Health": "OK",
"State": "Enabled"
},
"Thresholds": {
"LowerCaution": {
"Reading": 5
},
"LowerCritical": {
"Reading": 0
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/FanBay1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,26 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "FanBay2",
"Name": "Chassis Fan #2",
"PhysicalContext": "Chassis",
"Reading": 45,
"SpeedRPM": 2100,
"ReadingRangeMax": 100,
"ReadingRangeMin": 0,
"ReadingType": "Percent",
"ReadingUnits": "%",
"Status": {
"Health": "OK",
"State": "Enabled"
},
"Thresholds": {
"LowerCaution": {
"Reading": 5
},
"LowerCritical": {
"Reading": 0
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/FanBay2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,16 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "IntakeTemp",
"Name": "Front Panel Intake Temperature",
"PhysicalContext": "Intake",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 24.8,
"ReadingUnits": "Cel",
"ReadingRangeMin": 0,
"ReadingRangeMax": 75,
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/IntakeTemp",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,21 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Energy",
"Name": "Power Supply #1 Energy",
"ReadingType": "EnergykWh",
"ElectricalContext": "Total",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 7855,
"ReadingUnits": "kW.h",
"PhysicalContext": "PowerSupply",
"Actions": {
"#Sensor.ResetMetrics": {
"target": "/redfish/v1/Chassis/1U/Sensors/PS1Energy/Actions/Sensor.ResetMetrics"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Energy",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,40 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Frequency",
"Name": "Power Supply #1 Frequency",
"ReadingType": "Frequency",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 60.1,
"ReadingUnits": "Hz",
"ReadingRangeMin": 0,
"ReadingRangeMax": 160,
"Accuracy": 0.02,
"Precision": 2,
"SensingInterval": "PT0.125S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"Thresholds": {
"UpperCritical": {
"Reading": 60.37,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 60.2,
"DwellTime": "PT10M"
},
"LowerCaution": {
"Reading": 59.5,
"DwellTime": "PT5M"
},
"LowerCritical": {
"Reading": 58.75,
"DwellTime": "PT1M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Frequency",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,34 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1InputCurrent",
"Name": "Power Supply #1 Input Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 8.92,
"ReadingUnits": "A",
"ReadingRangeMin": 0,
"ReadingRangeMax": 40,
"Accuracy": 0.05,
"Precision": 2,
"SensingInterval": "PT0.1S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"Thresholds": {
"UpperCritical": {
"Reading": 10,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 9,
"Activation": "Increasing",
"DwellTime": "PT10M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1InputCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,36 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1InputPower",
"Name": "Power Supply #1 Input Power",
"ReadingType": "Power",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ElectricalContext": "Total",
"Reading": 374,
"ReadingUnits": "W",
"ReadingRangeMin": 0,
"ReadingRangeMax": 500,
"ApparentVA": 350,
"ReactiveVAR": 29,
"PowerFactor": 0.83,
"Accuracy": 1,
"Precision": 1,
"SensingInterval": "PT0.1S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"Thresholds": {
"UpperCritical": {
"Reading": 525,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 510,
"DwellTime": "PT10M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1InputPower",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,41 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1InputVoltage",
"Name": "Power Supply #1 Input Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ElectricalContext": "Total",
"Reading": 119.27,
"ReadingUnits": "V",
"ReadingRangeMin": 0,
"ReadingRangeMax": 260,
"Accuracy": 0.02,
"Precision": 2,
"SensingInterval": "PT0.125S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"Thresholds": {
"UpperCritical": {
"Reading": 125,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 122,
"DwellTime": "PT10M"
},
"LowerCaution": {
"Reading": 118,
"DwellTime": "PT5M"
},
"LowerCritical": {
"Reading": 115,
"DwellTime": "PT1M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1InputVoltage",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,41 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Out12V",
"Name": "Power Supply #1 12V Output Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ElectricalContext": "Total",
"Reading": 12.08,
"ReadingUnits": "V",
"ReadingRangeMin": 0,
"ReadingRangeMax": 15,
"Accuracy": 0.02,
"Precision": 2,
"SensingInterval": "PT0.125S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"Thresholds": {
"UpperCritical": {
"Reading": 12.5,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 12.35,
"DwellTime": "PT10M"
},
"LowerCaution": {
"Reading": 11.85,
"DwellTime": "PT5M"
},
"LowerCritical": {
"Reading": 11.5,
"DwellTime": "PT1M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out12V",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,33 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Out12VCurrent",
"Name": "Power Supply #1 12V Output Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 2.79,
"ReadingUnits": "A",
"ReadingRangeMin": 0,
"ReadingRangeMax": 40,
"Accuracy": 0.05,
"Precision": 2,
"SensingInterval": "PT0.1S",
"PhysicalContext": "Chassis",
"Thresholds": {
"UpperCritical": {
"Reading": 7,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 6,
"Activation": "Increasing",
"DwellTime": "PT10M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out12VCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,41 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Out3V",
"Name": "Power Supply #1 3V Output Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ElectricalContext": "Total",
"Reading": 3.32,
"ReadingUnits": "V",
"ReadingRangeMin": 0,
"ReadingRangeMax": 5,
"Accuracy": 0.02,
"Precision": 2,
"SensingInterval": "PT0.125S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"Thresholds": {
"UpperCritical": {
"Reading": 3.5,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 3.35,
"DwellTime": "PT10M"
},
"LowerCaution": {
"Reading": 3.25,
"DwellTime": "PT5M"
},
"LowerCritical": {
"Reading": 3.1,
"DwellTime": "PT1M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out3V",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,34 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Out3VCurrent",
"Name": "Power Supply #1 3V Output Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 8.92,
"ReadingUnits": "A",
"ReadingRangeMin": 0,
"ReadingRangeMax": 40,
"Accuracy": 0.05,
"Precision": 2,
"SensingInterval": "PT0.1S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"Thresholds": {
"UpperCritical": {
"Reading": 25,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 20,
"Activation": "Increasing",
"DwellTime": "PT10M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out3VCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,41 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Out5V",
"Name": "Power Supply #1 5V Output Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ElectricalContext": "Total",
"Reading": 5.04,
"ReadingUnits": "V",
"ReadingRangeMin": 0,
"ReadingRangeMax": 10,
"Accuracy": 0.02,
"Precision": 2,
"SensingInterval": "PT0.125S",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"Thresholds": {
"UpperCritical": {
"Reading": 5.5,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 5.35,
"DwellTime": "PT10M"
},
"LowerCaution": {
"Reading": 4.9,
"DwellTime": "PT5M"
},
"LowerCritical": {
"Reading": 4.5,
"DwellTime": "PT1M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out5V",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,33 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS1Out5VCurrent",
"Name": "Power Supply #1 5V Output Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 3.41,
"ReadingUnits": "A",
"ReadingRangeMin": 0,
"ReadingRangeMax": 10,
"Accuracy": 0.05,
"Precision": 2,
"SensingInterval": "PT0.1S",
"PhysicalContext": "PowerSupply",
"Thresholds": {
"UpperCritical": {
"Reading": 8,
"Activation": "Increasing",
"DwellTime": "PT1M"
},
"UpperCaution": {
"Reading": 5,
"Activation": "Increasing",
"DwellTime": "PT10M"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out5VCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,18 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Energy",
"Name": "Power Supply #2 Energy",
"ReadingType": "EnergykWh",
"ElectricalContext": "Total",
"Status": {
"State": "Absent"
},
"PhysicalContext": "PowerSupply",
"Actions": {
"#Sensor.ResetMetrics": {
"target": "/redfish/v1/Chassis/1U/Sensors/PS2Energy/Actions/Sensor.ResetMetrics"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Energy",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,13 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Frequency",
"Name": "Power Supply #2 Frequency",
"ReadingType": "Frequency",
"Status": {
"State": "Absent"
},
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Frequency",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,14 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2InputCurrent",
"Name": "Power Supply #2 Input Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Absent"
},
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2InputCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,14 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2InputPower",
"Name": "Power Supply #2 Input Power",
"ReadingType": "Power",
"Status": {
"State": "Absent"
},
"ElectricalContext": "Total",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2InputPower",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,14 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2InputVoltage",
"Name": "Power Supply #2 Input Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Absent"
},
"ElectricalContext": "Total",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Input",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2InputVoltage",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,14 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Out12V",
"Name": "Power Supply #2 12V Output Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Absent"
},
"ElectricalContext": "Total",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out12V",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,13 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Out12VCurrent",
"Name": "Power Supply #2 12V Output Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Absent"
},
"PhysicalContext": "PowerSupply",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out12VCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,14 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Out3V",
"Name": "Power Supply #2 3V Output Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Absent"
},
"ElectricalContext": "Total",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out3V",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,13 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Out3VCurrent",
"Name": "Power Supply #2 3V Output Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Absent"
},
"PhysicalContext": "PowerSupply",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out3VCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,14 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Out5V",
"Name": "Power Supply #2 5V Output Voltage",
"ReadingType": "Voltage",
"Status": {
"State": "Absent"
},
"ElectricalContext": "Total",
"PhysicalContext": "PowerSupply",
"PhysicalSubContext": "Output",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out5V",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,13 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "PS2Out5VCurrent",
"Name": "Power Supply #2 5V Output Current",
"ReadingType": "Current",
"ElectricalContext": "Total",
"Status": {
"State": "Absent"
},
"PhysicalContext": "PowerSupply",
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out5VCurrent",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,20 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "TotalEnergy",
"Name": "Total Energy",
"ReadingType": "EnergykWh",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 325675,
"ReadingUnits": "kW.h",
"PhysicalContext": "Chassis",
"Actions": {
"#Sensor.ResetMetrics": {
"target": "/redfish/v1/Chassis/1U/Sensors/TotalEnergy/Actions/Sensor.ResetMetrics"
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/TotalEnergy",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,53 @@
{
"@odata.type": "#Sensor.v1_14_0.Sensor",
"Id": "TotalPower",
"Name": "Power reading for the Chassis",
"ReadingType": "Power",
"ElectricalContext": "Total",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 374,
"ReadingUnits": "W",
"Implementation": "Synthesized",
"ReadingTime": "2019-08-13T04:14:33+06:00",
"ReadingRangeMin": 0,
"ReadingRangeMax": 600,
"ApparentVA": 2749.2,
"ReactiveVAR": 281.4,
"PowerFactor": 0.99,
"Accuracy": 0.2,
"Precision": 1,
"SensingInterval": "PT0.25S",
"PhysicalContext": "Chassis",
"Thresholds": {
"UpperCritical": {
"Reading": 600,
"Activation": "Increasing",
"DwellTime": "PT1M",
"HysteresisReading": -10,
"HysteresisDuration": "PT1M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
},
"UpperCaution": {
"Reading": 580,
"DwellTime": "PT10M",
"HysteresisReading": -10,
"HysteresisDuration": "PT10M",
"@Redfish.WriteableProperties": [
"Reading",
"DwellTime",
"HysteresisReading",
"HysteresisDuration"
]
}
},
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/TotalPower",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,132 @@
{
"@odata.type": "#SensorCollection.SensorCollection",
"Name": "Chassis sensors",
"Members@odata.count": 41,
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/AmbientTemp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPUFan1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPUFan2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/CPU1Temp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/DIMM1Temp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/DIMM2Temp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/DIMM3Temp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/ExhaustTemp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/FanBay1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/FanBay2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/IntakeTemp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Energy"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Frequency"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1InputCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1InputPower"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1InputVoltage"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out12V"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out12VCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out3V"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out3VCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out5V"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS1Out5VCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Energy"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Frequency"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2InputCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2InputPower"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2InputVoltage"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out12V"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out12VCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out3V"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out3VCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out5V"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/PS2Out5VCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/TotalEnergy"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/TotalPower"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1Temp"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1InputVoltage"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1InputCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1OutputVoltage"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1OutputCurrent"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Sensors/Battery1StateOfHealth"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Sensors",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,159 @@
{
"@odata.type": "#Thermal.v1_7_3.Thermal",
"Id": "Thermal",
"Name": "Thermal",
"Temperatures": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Temperatures/0",
"MemberId": "0",
"Name": "CPU1 Temp",
"SensorNumber": 5,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingCelsius": 41,
"UpperThresholdNonCritical": 42,
"UpperThresholdCritical": 45,
"UpperThresholdFatal": 48,
"MinReadingRangeTemp": 0,
"MaxReadingRangeTemp": 60,
"PhysicalContext": "CPU",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU1"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Temperatures/1",
"MemberId": "1",
"Name": "CPU2 Temp",
"SensorNumber": 6,
"Status": {
"State": "Disabled"
},
"UpperThresholdNonCritical": 42,
"UpperThresholdCritical": 45,
"UpperThresholdFatal": 48,
"MinReadingRangeTemp": 0,
"MaxReadingRangeTemp": 60,
"PhysicalContext": "CPU",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU2"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Temperatures/2",
"MemberId": "2",
"Name": "Chassis Intake Temp",
"SensorNumber": 9,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ReadingCelsius": 25,
"UpperThresholdNonCritical": 30,
"UpperThresholdCritical": 40,
"UpperThresholdFatal": 50,
"LowerThresholdNonCritical": 10,
"LowerThresholdCritical": 5,
"LowerThresholdFatal": 0,
"MinReadingRangeTemp": 0,
"MaxReadingRangeTemp": 60,
"PhysicalContext": "Intake",
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Chassis/1U"
},
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
}
]
}
],
"Fans": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/0",
"MemberId": "0",
"Name": "BaseBoard System Fan",
"PhysicalContext": "Backplane",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 2100,
"ReadingUnits": "RPM",
"LowerThresholdFatal": 0,
"MinReadingRange": 0,
"MaxReadingRange": 5000,
"Redundancy": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Redundancy/0"
}
],
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/1",
"MemberId": "1",
"Name": "BaseBoard System Fan Backup",
"PhysicalContext": "Backplane",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Reading": 2050,
"ReadingUnits": "RPM",
"LowerThresholdFatal": 0,
"MinReadingRange": 0,
"MaxReadingRange": 5000,
"Redundancy": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Redundancy/0"
}
],
"RelatedItem": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
]
}
],
"Redundancy": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Redundancy/0",
"MemberId": "0",
"Name": "BaseBoard System Fans",
"RedundancySet": [
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/0"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/Thermal#/Fans/1"
}
],
"Mode": "N+m",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"MinNumNeeded": 1,
"MaxNumSupported": 2
}
],
"@odata.id": "/redfish/v1/Chassis/1U/Thermal",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,31 @@
{
"@odata.type": "#Fan.v1_6_0.Fan",
"Id": "Bay1",
"Name": "Fan Bay 1",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"PhysicalContext": "Chassis",
"Model": "RKS-440DC",
"Manufacturer": "Contoso Fans",
"PartNumber": "23456-133",
"SparePartNumber": "93284-133",
"LocationIndicatorActive": true,
"HotPluggable": true,
"SpeedPercent": {
"Reading": 45,
"SpeedRPM": 2200,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/FanBay1"
},
"RatedSpeedRPM": 4900,
"Location": {
"PartLocation": {
"ServiceLabel": "Chassis Fan Bay 1",
"LocationType": "Bay",
"LocationOrdinalValue": 0
}
},
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,31 @@
{
"@odata.type": "#Fan.v1_6_0.Fan",
"Id": "Bay2",
"Name": "Fan Bay 2",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"PhysicalContext": "Chassis",
"Model": "RKS-440DC",
"Manufacturer": "Contoso Fans",
"PartNumber": "23456-133",
"SparePartNumber": "93284-133",
"LocationIndicatorActive": false,
"HotPluggable": true,
"SpeedPercent": {
"Reading": 45,
"SpeedRPM": 2400,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/FanBay2"
},
"RatedSpeedRPM": 4900,
"Location": {
"PartLocation": {
"ServiceLabel": "Chassis Fan Bay 2",
"LocationType": "Bay",
"LocationOrdinalValue": 1
}
},
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,31 @@
{
"@odata.type": "#Fan.v1_6_0.Fan",
"Id": "CPU1",
"Name": "Fan for CPU 1",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"PhysicalContext": "CPU",
"Model": "RKS-440DC",
"Manufacturer": "Contoso Fans",
"PartNumber": "23456-133",
"SparePartNumber": "93284-133",
"LocationIndicatorActive": false,
"HotPluggable": false,
"SpeedPercent": {
"Reading": 45,
"SpeedRPM": 1490,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/CPUFan1"
},
"RatedSpeedRPM": 3300,
"Location": {
"PartLocation": {
"ServiceLabel": "CPU #1 Fan",
"LocationType": "Bay",
"LocationOrdinalValue": 0
}
},
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU1",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,31 @@
{
"@odata.type": "#Fan.v1_6_0.Fan",
"Id": "CPU2",
"Name": "Fan for CPU 2",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"PhysicalContext": "CPU",
"Model": "RKS-440DC",
"Manufacturer": "Contoso Fans",
"PartNumber": "23456-133",
"SparePartNumber": "93284-133",
"LocationIndicatorActive": false,
"HotPluggable": false,
"SpeedPercent": {
"Reading": 45,
"SpeedRPM": 1490,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/CPUFan2"
},
"RatedSpeedRPM": 3300,
"Location": {
"PartLocation": {
"ServiceLabel": "CPU #2 Fan",
"LocationType": "Bay",
"LocationOrdinalValue": 1
}
},
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU2",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,21 @@
{
"@odata.type": "#FanCollection.FanCollection",
"Name": "Fan Club",
"Members@odata.count": 4,
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay2"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU2"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,37 @@
{
"@odata.type": "#ThermalMetrics.v1_3_3.ThermalMetrics",
"Id": "ThermalMetrics",
"Name": "Chassis Thermal Metrics",
"TemperatureSummaryCelsius": {
"Internal": {
"Reading": 39,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/CPU1Temp"
},
"Intake": {
"Reading": 24.8,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/IntakeTemp"
},
"Ambient": {
"Reading": 22.5,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/AmbientTemp"
},
"Exhaust": {
"Reading": 40.5,
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/ExhaustTemp"
}
},
"TemperatureReadingsCelsius": [
{
"Reading": 24.8,
"DeviceName": "Intake",
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/IntakeTemp"
},
{
"Reading": 40.5,
"DeviceName": "Exhaust",
"DataSourceUri": "/redfish/v1/Chassis/1U/Sensors/ExhaustTemp"
}
],
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/ThermalMetrics",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,57 @@
{
"@odata.type": "#ThermalSubsystem.v1_5_0.ThermalSubsystem",
"Id": "ThermalSubsystem",
"Name": "Thermal Subsystem for Chassis",
"FanRedundancy": [
{
"RedundancyType": "NPlusM",
"MaxSupportedInGroup": 2,
"MinNeededInGroup": 1,
"MinNeededForFaultTolerance": 2,
"RedundancyGroup": [
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/Bay2"
}
],
"Status": {
"State": "Enabled",
"Health": "OK"
}
},
{
"RedundancyType": "NPlusM",
"MaxSupportedInGroup": 2,
"MinNeededInGroup": 1,
"MinNeededForFaultTolerance": 2,
"RedundancyGroup": [
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU1"
},
{
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans/CPU2"
}
],
"Status": {
"State": "Disabled"
}
}
],
"Fans": {
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Fans"
},
"ThermalMetrics": {
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/ThermalMetrics"
},
"Heaters": {
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem/Heaters"
},
"Status": {
"State": "Enabled",
"Health": "OK"
},
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,104 @@
{
"@odata.type": "#Chassis.v1_29_0.Chassis",
"Id": "1U",
"Name": "Computer System Chassis",
"ChassisType": "RackMount",
"AssetTag": "Portland-45Z-2381",
"Manufacturer": "Contoso",
"Model": "3500RX",
"SKU": "8675309",
"SerialNumber": "437XR1138R2",
"PartNumber": "224071-J23",
"PowerState": "On",
"LocationIndicatorActive": true,
"RackUnits": "EIA_310",
"HeightRackUnits": 1,
"HeightMm": 44.45,
"WidthMm": 431.8,
"DepthMm": 711,
"WeightKg": 15.31,
"Location": {
"PhysicalAddress": {
"Country": "US",
"StateOrProvince": "Oregon",
"City": "Portland",
"StreetAddress": "1001 SW 5th Avenue",
"PostalCode": "97204",
"ISOCountryCode": "USA",
"ISOSubdivisionCode": "OR"
},
"Placement": {
"Row": "North",
"Rack": "WEB43",
"RackOffsetUnits": "EIA_310",
"RackOffset": 12,
"Room": "35",
"FacilityName": "Onsite-Main"
}
},
"Status": {
"State": "Enabled",
"Health": "OK"
},
"ThermalSubsystem": {
"@odata.id": "/redfish/v1/Chassis/1U/ThermalSubsystem"
},
"PowerSubsystem": {
"@odata.id": "/redfish/v1/Chassis/1U/PowerSubsystem"
},
"EnvironmentMetrics": {
"@odata.id": "/redfish/v1/Chassis/1U/EnvironmentMetrics"
},
"Sensors": {
"@odata.id": "/redfish/v1/Chassis/1U/Sensors"
},
"Controls": {
"@odata.id": "/redfish/v1/Chassis/1U/Controls"
},
"TrustedComponents": {
"@odata.id": "/redfish/v1/Chassis/1U/TrustedComponents"
},
"Thermal@Redfish.Deprecated": "Please migrate to use /redfish/v1/Chassis/1U/ThermalSubsystem",
"Thermal": {
"@odata.id": "/redfish/v1/Chassis/1U/Thermal"
},
"Power@Redfish.Deprecated": "Please migrate to use /redfish/v1/Chassis/1U/PowerSubsystem",
"Power": {
"@odata.id": "/redfish/v1/Chassis/1U/Power"
},
"Links": {
"ComputerSystems": [
{
"@odata.id": "/redfish/v1/Systems/437XR1138R2"
}
],
"ManagedBy": [
{
"@odata.id": "/redfish/v1/Managers/BMC"
}
],
"ManagersInChassis": [
{
"@odata.id": "/redfish/v1/Managers/BMC"
}
]
},
"Actions": {
"#Chassis.Reset": {
"target": "/redfish/v1/Chassis/1U/Actions/Chassis.Reset",
"ResetType@Redfish.AllowableValues": [
"On",
"ForceOff",
"GracefulShutdown",
"GracefulRestart",
"ForceRestart",
"Nmi",
"ForceOn",
"PushPowerButton",
"FullPowerCycle"
]
}
},
"@odata.id": "/redfish/v1/Chassis/1U",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,12 @@
{
"@odata.type": "#ChassisCollection.ChassisCollection",
"Name": "Chassis Collection",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Chassis/1U"
}
],
"@odata.id": "/redfish/v1/Chassis",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
+29
View File
@@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2014-2025, Contributing Member(s) of Distributed Management Task
Force, Inc.. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,57 @@
{
"@odata.type": "#EthernetInterface.v1_12_6.EthernetInterface",
"Id": "ToHost",
"Name": "Manager Ethernet Interface",
"Description": "Management Network Interface",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"InterfaceEnabled": true,
"PermanentMACAddress": "AA:BB:CC:DD:EE:FF",
"MACAddress": "AA:BB:CC:DD:EE:FF",
"SpeedMbps": 100,
"AutoNeg": true,
"FullDuplex": true,
"MTUSize": 1500,
"HostName": "bmc-host-interface",
"FQDN": "bmc-host-interface.dmtf.org",
"MaxIPv6StaticAddresses": 1,
"IPv4Addresses": [
{
"Address": "192.168.20.55",
"SubnetMask": "255.255.255.0",
"AddressOrigin": "Static",
"Gateway": "192.168.20.1"
}
],
"IPv6AddressPolicyTable": [
{
"Prefix": "::1/128",
"Precedence": 50,
"Label": 0
}
],
"IPv6StaticAddresses": [
{
"Address": "fe80::1ec1:deff:fe6f:1e24",
"PrefixLength": 64
}
],
"IPv6DefaultGateway": "fe80::1ec1:deff:fe6f:1e24",
"IPv6Addresses": [
{
"Address": "fe80::1ec1:deff:fe6f:1e24",
"PrefixLength": 64,
"AddressOrigin": "Static",
"AddressState": "Preferred"
}
],
"Links": {
"HostInterface": {
"@odata.id": "/redfish/v1/Managers/BMC/HostInterfaces/1"
}
},
"@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces/ToHost",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,83 @@
{
"@odata.type": "#EthernetInterface.v1_12_6.EthernetInterface",
"Id": "eth0",
"Name": "Manager Ethernet Interface",
"Description": "Management network interface on a dedicated port",
"Status": {
"State": "Enabled",
"Health": "OK"
},
"LinkStatus": "LinkUp",
"InterfaceEnabled": true,
"PermanentMACAddress": "23:11:8A:33:CF:EA",
"MACAddress": "23:11:8A:33:CF:EA",
"SpeedMbps": 100,
"AutoNeg": true,
"FullDuplex": true,
"MTUSize": 1500,
"HostName": "web483-bmc",
"FQDN": "web483-bmc.dmtf.org",
"MaxIPv6StaticAddresses": 1,
"VLAN": {
"VLANEnable": true,
"VLANId": 101
},
"IPv4Addresses": [
{
"Address": "192.168.0.10",
"SubnetMask": "255.255.252.0",
"AddressOrigin": "DHCP",
"Gateway": "192.168.0.1"
}
],
"IPv6AddressPolicyTable": [
{
"Prefix": "::1/128",
"Precedence": 50,
"Label": 0
}
],
"IPv6StaticAddresses": [
{
"Address": "fe80::1ec1:deff:fe6f:1e24",
"PrefixLength": 16
}
],
"IPv6DefaultGateway": "fe80::1ec1:deff:fe6f:1e24",
"IPv6Addresses": [
{
"Address": "fe80::1ec1:deff:fe6f:1e24",
"PrefixLength": 64,
"AddressOrigin": "SLAAC",
"AddressState": "Preferred"
}
],
"NameServers": [
"names.dmtf.org"
],
"Links": {
"Ports": [
{
"@odata.id": "/redfish/v1/Managers/BMC/DedicatedNetworkPorts/1"
}
]
},
"@Redfish.Settings": {
"@odata.type": "#Settings.v1_5_0.Settings",
"SettingsObject": {
"@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces/eth0/SD"
},
"Time": "2012-03-07T14:44.30-05:00",
"ETag": "84ffcbb050ddc7fa9cddb59014546e59",
"Messages": [
{
"MessageId": "Base.1.0.SettingsFailed",
"RelatedProperties": [
"/IPv6Addresses/PrefixLength"
]
}
]
},
"@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces/eth0",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,16 @@
{
"@odata.type": "#EthernetInterfaceCollection.EthernetInterfaceCollection",
"Name": "Ethernet Network Interface Collection",
"Description": "Collection of EthernetInterfaces for this Manager",
"Members@odata.count": 2,
"Members": [
{
"@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces/eth0"
},
{
"@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces/ToHost"
}
],
"@odata.id": "/redfish/v1/Managers/BMC/EthernetInterfaces",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,30 @@
{
"@odata.type": "#LogEntryCollection.LogEntryCollection",
"Name": "Log Service Collection",
"Description": "Collection of Logs for this System",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Managers/BMC/LogServices/Log/Entries/1",
"@odata.type": "#LogEntry.v1_22_0.LogEntry",
"Id": "1",
"Name": "Log Entry 1",
"EntryType": "Event",
"Severity": "Critical",
"Created": "2012-03-07T14:44:00Z",
"SensorNumber": 1,
"Message": "System May be Melting",
"MessageId": "Event.1.0.TempWayTooHot",
"MessageArgs": [
"88"
],
"Links": {
"OriginOfCondition": {
"@odata.id": "/redfish/v1/Chassis/1U/Thermal"
}
}
}
],
"@odata.id": "/redfish/v1/Managers/BMC/LogServices/Log/Entries",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,24 @@
{
"@odata.type": "#LogService.v1_9_0.LogService",
"Id": "Log",
"Name": "System Log Service",
"MaxNumberOfRecords": 1000,
"OverWritePolicy": "WrapsWhenFull",
"DateTime": "2015-03-13T04:14:33+06:00",
"DateTimeLocalOffset": "+06:00",
"ServiceEnabled": true,
"Status": {
"State": "Enabled",
"Health": "OK"
},
"Actions": {
"#LogService.ClearLog": {
"target": "/redfish/v1/Managers/BMC/LogServices/Log/Actions/LogService.ClearLog"
}
},
"Entries": {
"@odata.id": "/redfish/v1/Managers/BMC/LogServices/Log/Entries"
},
"@odata.id": "/redfish/v1/Managers/BMC/LogServices/Log",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
@@ -0,0 +1,13 @@
{
"@odata.type": "#LogServiceCollection.LogServiceCollection",
"Name": "Log Service Collection",
"Description": "Collection of Log Services for this Manager",
"Members@odata.count": 1,
"Members": [
{
"@odata.id": "/redfish/v1/Managers/BMC/LogServices/Log"
}
],
"@odata.id": "/redfish/v1/Managers/BMC/LogServices",
"@Redfish.Copyright": "Copyright 2014-2026 DMTF. For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright."
}
Loaded 100 of 147 files, more files were not shown because too many files have changed in this diff. Show more