Merge pull request #1 from chrismanivong/feat/redfish-client
Release 0.1.0: Redfish driver on ManagementControllerDriver
This commit is contained in:
147 files changed
+9283
-5
No files matched your search
@@ -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)
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""NAPALM driver for server BMCs over DMTF Redfish."""
|
||||
|
||||
from napalm_redfish.driver import RedfishDriver
|
||||
|
||||
__version__ = "0.1.0"
|
||||
__all__ = ["RedfishDriver"]
|
||||
@@ -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
|
||||
@@ -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",
|
||||
]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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"),
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
|
||||
@@ -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."""
|
||||
@@ -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"]
|
||||
@@ -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")
|
||||
@@ -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
|
||||
+40
@@ -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."
|
||||
}
|
||||
+46
@@ -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."
|
||||
}
|
||||
+49
@@ -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."
|
||||
}
|
||||
+46
@@ -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."
|
||||
}
|
||||
@@ -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.
|
||||
+73
@@ -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."
|
||||
}
|
||||
+12
@@ -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
@@ -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."
|
||||
}
|
||||
+85
@@ -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."
|
||||
}
|
||||
Vendored
+99
@@ -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."
|
||||
}
|
||||
Vendored
+19
@@ -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."
|
||||
}
|
||||
+15
@@ -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."
|
||||
}
|
||||
+16
@@ -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."
|
||||
}
|
||||
+30
@@ -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."
|
||||
}
|
||||
+30
@@ -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."
|
||||
}
|
||||
+30
@@ -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."
|
||||
}
|
||||
+30
@@ -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."
|
||||
}
|
||||
+24
@@ -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."
|
||||
}
|
||||
+29
@@ -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."
|
||||
}
|
||||
+33
@@ -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."
|
||||
}
|
||||
+71
@@ -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."
|
||||
}
|
||||
+71
@@ -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."
|
||||
}
|
||||
+71
@@ -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."
|
||||
}
|
||||
+16
@@ -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."
|
||||
}
|
||||
+16
@@ -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."
|
||||
}
|
||||
+21
@@ -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."
|
||||
}
|
||||
+40
@@ -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."
|
||||
}
|
||||
+34
@@ -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."
|
||||
}
|
||||
+36
@@ -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."
|
||||
}
|
||||
+41
@@ -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."
|
||||
}
|
||||
+41
@@ -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."
|
||||
}
|
||||
+33
@@ -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."
|
||||
}
|
||||
+34
@@ -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."
|
||||
}
|
||||
+33
@@ -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."
|
||||
}
|
||||
+18
@@ -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."
|
||||
}
|
||||
+13
@@ -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."
|
||||
}
|
||||
+14
@@ -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."
|
||||
}
|
||||
+14
@@ -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."
|
||||
}
|
||||
+14
@@ -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."
|
||||
}
|
||||
+14
@@ -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."
|
||||
}
|
||||
+13
@@ -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."
|
||||
}
|
||||
+13
@@ -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."
|
||||
}
|
||||
+13
@@ -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."
|
||||
}
|
||||
+20
@@ -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."
|
||||
}
|
||||
+53
@@ -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."
|
||||
}
|
||||
+31
@@ -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."
|
||||
}
|
||||
+31
@@ -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."
|
||||
}
|
||||
+31
@@ -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."
|
||||
}
|
||||
+31
@@ -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."
|
||||
}
|
||||
+21
@@ -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."
|
||||
}
|
||||
Vendored
+37
@@ -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."
|
||||
}
|
||||
@@ -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.
|
||||
+57
@@ -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."
|
||||
}
|
||||
+83
@@ -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."
|
||||
}
|
||||
+16
@@ -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."
|
||||
}
|
||||
+30
@@ -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."
|
||||
}
|
||||
+24
@@ -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
Reference in new issue
Block a user