diff --git a/python/redfish-api/redfish_exporter_v9000.py b/python/redfish-api/redfish_exporter_v9000.py index c91b5d8..3c954af 100644 --- a/python/redfish-api/redfish_exporter_v9000.py +++ b/python/redfish-api/redfish_exporter_v9000.py @@ -19,6 +19,22 @@ from prometheus_client import ( ) +@dataclass +class RedfishResource: + """Container for Redfish resource URLs.""" + chassis: str | None = None + systems: str | None = None + power: str | None = None + session_service: str | None = None + +@dataclass +class PowerMetrics: + """Container for power metrics.""" + voltage: float | None = None + watts: float | None = None + amps: float | None = None + serial: str | None = None + @dataclass class RedfishSession: """Container for Redfish session data.""" @@ -132,8 +148,8 @@ async def login_hpe(session, host: HostConfig) -> bool: try: async with session.post(login_url, json=payload, ssl=False, timeout=10) as login_resp: if login_resp.status == 201: - host.session_token = login_resp.headers.get("X-Auth-Token") - host.session_logout = login_resp.headers.get("Location") + host.session.token = login_resp.headers.get("X-Auth-Token") + host.session.logout_url = login_resp.headers.get("Location") if not host.session.token or not host.session.logout_url: raise RuntimeError("Invalid login response") @@ -228,23 +244,25 @@ async def fetch_with_retry(session, host: HostConfig, url: str) -> dict | None: return None -async def discover_redfish_resources(session, host: HostConfig) -> dict: +async def discover_redfish_resources(session, host: HostConfig) -> RedfishResource | None: """Discover available Redfish resources and return relevant URLs""" root_url = f"https://{host.fqdn}/redfish/v1/" data = await fetch_with_retry(session, host, root_url) if not data: return {} - # Extrahiere Links aus der Root-Antwort - links = { - "Chassis": data.get("Chassis", {}).get("@odata.id"), - "Systems": data.get("Systems", {}).get("@odata.id"), - "SessionService": data.get("SessionService", {}).get("@odata.id"), - } - if not links["Chassis"]: + # Create RedfishRessource object + resources = RedfishResource( + chassis=data.get("Chassis", {}).get("@odata.id"), + systems=data.get("Systems", {}).get("@odata.id"), + session_service=data.get("SessionService", {}).get("@odata.id"), + ) + + if not resources.chassis: logging.error("No valid Chassis URL found for host %s", host.fqdn) - return {} - return links + return None + + return resources def get_power_resource_info( @@ -313,54 +331,46 @@ def process_power_supplies( async def process_power_supply( session, host: HostConfig, psu_data: dict, power_resource_type: str -): +) -> PowerMetrics | None: """Extract metrics from PowerSupply""" serial = psu_data.get("SerialNumber") + metrics = PowerMetrics(serial=serial) if power_resource_type == "PowerSubsystem": - # Newer Redfish API: Metrics are an own "Metrics" ressource + # New Redfish API: Metrics are an own "Metrics" ressource metrics_url = psu_data.get("Metrics", {}).get("@odata.id") if not metrics_url: logging.warning("No Metrics found for PowerSupply %s", psu_data.get("Id")) - return + return None metrics_url = f"https://{host.fqdn}{metrics_url}" metrics_data = await fetch_with_retry(session, host, metrics_url) if not metrics_data: - return + return None # Get metrics from Metrics ressource - line_input_v = metrics_data.get("InputVoltage", {}).get("Reading") - watts_input = metrics_data.get("InputPowerWatts", {}).get("Reading") - amps_input = metrics_data.get("InputCurrentAmps", {}).get("Reading") + metrics.voltage = metrics_data.get("InputVoltage", {}).get("Reading") + metrics.watts = metrics_data.get("InputPowerWatts", {}).get("Reading") + metrics.amps = metrics_data.get("InputCurrentAmps", {}).get("Reading") elif power_resource_type == "Power": # Older Redfish API: Metrics are direct in PowerSupply as an array - line_input_v = psu_data.get("LineInputVoltage") - watts_input = psu_data.get("PowerInputWatts") - if watts_input is None: - watts_input = psu_data.get("LastPowerOutputWatts") - amps_input = psu_data.get("InputCurrentAmps") - if amps_input is None: - if line_input_v and watts_input: - amps_input = round(watts_input / line_input_v, 2) + metrics.voltage = psu_data.get("LineInputVoltage") + metrics.watts = psu_data.get("PowerInputWatts") + if metrics.watts is None: + metrics.watts = psu_data.get("LastPowerOutputWatts") + metrics.amps = psu_data.get("InputCurrentAmps") + if metrics.amps is None and metrics.voltage and metrics.watts: + metrics.amps = round(metrics.watts / metrics.voltage, 2) else: logging.error( "Unknown power resource type for PowerSupply %s", psu_data.get("Id") ) - return - if amps_input is None and line_input_v and watts_input: - amps_input = round(watts_input / line_input_v, 2) + return None - # Update Prometheus metrics - if line_input_v is not None: - VOLTAGE_GAUGE.labels(host=host.fqdn, psu_serial=serial).set(line_input_v) - if watts_input is not None: - WATTS_GAUGE.labels(host=host.fqdn, psu_serial=serial).set(watts_input) - if amps_input is not None: - AMPS_GAUGE.labels(host=host.fqdn, psu_serial=serial).set(amps_input) + return metrics def normalize_url(url: str) -> str: