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
|
boards that predate Redfish. Built on
|
||||||
[napalm-device-types](https://git.netork.io/NAPALM/napalm-device-types). Written for netOrk.
|
[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
|
**Status: early development.** The `redfish` driver (`napalm_redfish.RedfishDriver`, a
|
||||||
first test device is a Supermicro X10DRL-i (BMC firmware 3.91, Redfish).
|
`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
|
## 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
|
- `napalm-device-types` is not on PyPI: install it from
|
||||||
`git+https://git.netork.io/NAPALM/napalm-device-types.git` before this package.
|
`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
|
## License
|
||||||
|
|
||||||
Apache License 2.0, see [LICENSE](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 |
|
| `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) |
|
| `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
|
The transport uses the thin `redfish` client, which keeps quirk handling in this driver;
|
||||||
quirk handling in this driver; `sushy` brings an object model but also its own assumptions
|
`sushy` brings an object model but also its own assumptions about Ironic's needs. Note that
|
||||||
about Ironic's needs.
|
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
|
## 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