Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _item_name(item_data, padding):

def inventorize_redfish_firmware(section: RedfishAPIData) -> InventoryResult:
"""create inventory table for firmware"""
path = ["hardware", "firmware", "redfish"]
path = ["hardware", "firmware"]
if section.get("FirmwareInventory", {}).get("Current"):
data = section.get("FirmwareInventory", {}).get("Current")
padding = len(str(len(data)))
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
"""Redfish HW/SW inventory plugin"""

# (c) Andreas Doehler <andreas.doehler@bechtle.com/andreas.doehler@gmail.com>
# License: GNU General Public License v2

from collections.abc import Mapping, Sequence
from typing import Any

from cmk.agent_based.v2 import InventoryPlugin, InventoryResult, TableRow
from cmk.plugins.redfish.lib import RedfishAPIData

IDSET = set[tuple[str, str, str, str, str, str]]


def _extract_odata_ids(
data: None | RedfishAPIData | Sequence[Mapping[str, Any]], ids_set: IDSET
) -> IDSET:
if data is None or isinstance(data, (str, int, float, bool)):
return ids_set

if isinstance(data, Mapping):
for key, value in data.items():
if key == "@odata.id" and isinstance(value, str):
if "Oem" not in value:
key_name = data.get("Name")
if key_name:
ids_set.add(
(
value,
key_name,
(data.get("SerialNumber") or "nothing set").strip(),
(data.get("PartNumber") or "nothing set").strip(),
(data.get("Manufacturer") or "nothing set").strip(),
(data.get("Model") or "nothing set").strip(),
)
)
else:
ids_set = _extract_odata_ids(value, ids_set)
else:
for item in data:
ids_set = _extract_odata_ids(item, ids_set)

return ids_set


def inventory_redfish_data(
section_redfish_storage: None | RedfishAPIData,
section_redfish_processors: None | RedfishAPIData,
section_redfish_drives: None | RedfishAPIData,
section_redfish_psu: None | RedfishAPIData,
section_redfish_memory: None | RedfishAPIData,
section_redfish_power: None | RedfishAPIData,
section_redfish_thermal: None | RedfishAPIData,
section_redfish_networkadapters: None | RedfishAPIData,
) -> InventoryResult:
result_path = ["redfish"]

odata_ids_set: IDSET = set()
odata_ids_set = _extract_odata_ids(section_redfish_processors, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_storage, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_drives, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_psu, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_memory, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_power, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_thermal, odata_ids_set)
odata_ids_set = _extract_odata_ids(section_redfish_networkadapters, odata_ids_set)

for path, name, serial, part_number, manufacturer, model in odata_ids_set:
if (
serial in ("nothing set", "NOT AVAILABLE")
and part_number in ("nothing set", "NOT AVAILABLE")
and manufacturer == "nothing set"
and model == "nothing set"
):
continue
if path.startswith("/redfish/"):
segments = (
path.replace("#", "")
.replace(":", "-")
.replace(".", "_")
.replace("'", "")
.replace("(", "_")
.replace(")", "_")
.replace("%", "_")
.strip("/")
.split("/")
)
result_path = [element for element in segments if element != ""]
item_id = result_path.pop()
if result_path[0] == "redfish":
result_path = result_path[1:]
if result_path[0] == "v1":
result_path = result_path[1:]
final_path = ["hardware"] + result_path
yield TableRow(
path=final_path,
key_columns={"id": item_id},
inventory_columns={
"name": name,
"serial": serial,
"part_number": part_number,
"manufacturer": manufacturer,
"model": model,
},
)


inventory_plugin_redfish_data = InventoryPlugin(
name="redfish_data",
sections=[
"redfish_storage",
"redfish_processors",
"redfish_drives",
"redfish_psu",
"redfish_memory",
"redfish_power",
"redfish_thermal",
"redfish_networkadapters",
],
inventory_function=inventory_redfish_data,
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
CheckPlugin,
CheckResult,
DiscoveryResult,
render,
Result,
Service,
State,
Expand Down Expand Up @@ -52,37 +53,61 @@ def discovery_redfish_ethernetinterfaces(
continue
if section[key].get("LinkStatus", "NOLINK") in ["LinkUp"] and disc_state == "down":
continue
yield Service(item=section[key]["Id"])
yield Service(
item=section[key]["Id"],
parameters={
"discover_speed": section[key].get("SpeedMbps", 0)
if "SpeedMbps" in section[key]
else section[key].get("CurrentLinkSpeedMbps", 0),
"discover_link_status": section[key].get("LinkStatus", "NOLINK"),
},
)


def check_redfish_ethernetinterfaces(item: str, section: RedfishAPIData) -> CheckResult:
def _render_speed(speed: int) -> str:
factor = 1_000_000
return render.networkbandwidth(speed / 8 * factor)


def check_redfish_ethernetinterfaces(
item: str, params: Mapping[str, Any], section: RedfishAPIData
) -> CheckResult:
"""Check single interfaces"""
data = section.get(item, None)
if data is None:
return

mac_addr = ""
link_state = State.OK
link_summary = "Link: No info"
if (link_status := data.get("LinkStatus")) is not None:
link_summary = f"Link: {link_status}"
if (discover_link_changed := params.get("discover_link_status")) != link_status:
link_state = State(params.get("state_if_link_status_changed") or 0)
link_summary = f"Link: {link_status} (changed from {discover_link_changed})"
yield Result(state=link_state, summary=link_summary)

speed_state = State.OK
speed_summary = "Speed: No info"
link_speed: int | None = data.get("CurrentLinkSpeedMbps")
if link_speed is None: # Prioritize CurrentLinkSpeedMbps, fallback to SpeedMbps
link_speed = data.get("SpeedMbps")

if link_speed is not None:
speed_summary = f"Speed: {_render_speed(link_speed)}"
if (discover_speed := params.get("discover_speed") or 0) != link_speed:
speed_state = State(params.get("state_if_link_speed_changed") or 0)
speed_summary = (
f"Speed: {_render_speed(link_speed)} (changed from {_render_speed(discover_speed)})"
)
yield Result(state=speed_state, summary=speed_summary)

mac_addr = None
if data.get("AssociatedNetworkAddresses"):
mac_addr = ", ".join(data.get("AssociatedNetworkAddresses"))
elif data.get("MACAddress"):
mac_addr = data.get("MACAddress")

link_speed = 0
if data.get("CurrentLinkSpeedMbps"):
link_speed = data.get("CurrentLinkSpeedMbps")
elif data.get("SpeedMbps"):
link_speed = data.get("SpeedMbps")
if link_speed is None:
link_speed = 0

link_status = "Unknown"
if data.get("LinkStatus"):
link_status = data.get("LinkStatus")
if link_status is None:
link_status = "Down"

int_msg = f"Link: {link_status}, Speed: {link_speed:0.0f}Mbps, MAC: {mac_addr}"
yield Result(state=State(0), summary=int_msg)
if mac_addr:
yield Result(state=State.OK, summary=f"MAC Address: {mac_addr}")

if data.get("Status"):
dev_state, dev_msg = redfish_health_state(data.get("Status", {}))
Expand All @@ -103,4 +128,6 @@ def check_redfish_ethernetinterfaces(item: str, section: RedfishAPIData) -> Chec
discovery_ruleset_name="discovery_redfish_ethernetinterfaces",
discovery_default_parameters={"state": "updown"},
check_function=check_redfish_ethernetinterfaces,
check_default_parameters={},
check_ruleset_name="check_redfish_ethernetinterfaces",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""check single redfish pdu state"""

# (c) Andreas Doehler <andreas.doehler@bechtle.com/andreas.doehler@gmail.com>
# License: GNU General Public License v2

from cmk.agent_based.v2 import (
AgentSection,
CheckPlugin,
CheckResult,
DiscoveryResult,
Result,
Service,
State,
)
from cmk.plugins.redfish.lib import (
parse_redfish_multiple,
redfish_health_state,
RedfishAPIData,
)

agent_section_redfish_pdus = AgentSection(
name="redfish_rackpdus",
parse_function=parse_redfish_multiple,
parsed_section_name="redfish_pdus",
)


def discovery_redfish_pdus(section: RedfishAPIData) -> DiscoveryResult:
"""Discover single pdus"""
for key in section.keys():
if section[key].get("Status", {}).get("State") == "Absent":
continue
item = key.split("/")[-1]
yield Service(item=item)


def check_redfish_pdus(item: str, section: RedfishAPIData) -> CheckResult:
"""Check single pdu state"""

for key in section.keys():
if key.endswith(f"/{item}"):
item = key
break
data = section.get(item, None)
if data is None:
return
print(data)
firmware = data.get("FirmwareVersion")
serial = data.get("SerialNumber")
model = data.get("Model")
manufacturer = data.get("Manufacturer")

yield Result(state=State.OK, summary=f"PDU {manufacturer} {model} S/N {serial} FW {firmware}")

dev_state, dev_msg = redfish_health_state(data.get("Status", {}))
yield Result(state=State(dev_state), summary=dev_msg)


check_plugin_redfish_pdus = CheckPlugin(
name="redfish_pdus",
service_name="PDU %s",
sections=["redfish_pdus"],
discovery_function=discovery_redfish_pdus,
check_function=check_redfish_pdus,
)
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,8 @@ def check_redfish_physicaldrives(item: str, section: RedfishAPIData) -> CheckRes
)
yield Metric("media_life_left", int(data.get("PredictedMediaLifeLeftPercent")))
elif data.get("SSDEnduranceUtilizationPercentage"):
disc_msg = (
f"{disc_msg}, SSD Utilization: "
f"{int(data.get('SSDEnduranceUtilizationPercentage', 0))}%"
)
media_life_left = 100 - int(data.get("SSDEnduranceUtilizationPercentage", 0))
disc_msg = f"{disc_msg}, Media Life Left: {media_life_left}%"
yield Metric("ssd_utilization", int(data.get("SSDEnduranceUtilizationPercentage")))
yield Result(state=State(0), summary=disc_msg)

Expand Down
Loading