-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add TTL cache to redfish discover method (#34)
The `redfish.discover_ssdp()` method takes a very long time to query the redfish services. This causes the scrape timeout to be exceeded for Prometheus. In order to solve this, a TTL cache has been added. A logging message has also been added for debugging. Additionally, the `redfish.redfish_client` call to create a redfish object has been provided with the `timeout` and `max_retry` arguments in order to not have the query take too long in case of incorrect redfish credentials. redfish_client config and discover cache parameters can be modified through the prometheus-hardware-exporter CLI args. The default values for these configs are defined in `config.py`. Some other helpful improvements: - `RedfishHelper` class now directly takes an instance of `Config` instead of sending the parameters individually. - Use context manager for redish login object to automatically logout at the end of the context. - Split sensor data methods to be more readable. - Add unit tests
- Loading branch information
Showing
8 changed files
with
301 additions
and
93 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,65 +1,94 @@ | ||
"""Redfish collector.""" | ||
from logging import getLogger | ||
from typing import Any, Dict, List, Optional | ||
from typing import Any, Callable, Dict, List | ||
|
||
import redfish | ||
import redfish_utilities | ||
from redfish.rest.v1 import HttpClient, InvalidCredentialsError, SessionCreationError | ||
from cachetools.func import ttl_cache | ||
from redfish.rest.v1 import ( | ||
InvalidCredentialsError, | ||
RetriesExhaustedError, | ||
SessionCreationError, | ||
) | ||
|
||
from prometheus_hardware_exporter.config import Config | ||
|
||
logger = getLogger(__name__) | ||
|
||
|
||
class RedfishHelper: | ||
"""Helper function for redfish.""" | ||
|
||
def get_sensor_data(self, host: str, username: str, password: str) -> Dict[str, List]: | ||
def __init__(self, config: Config) -> None: | ||
"""Initialize redfish login args and cache TTL value for discover method.""" | ||
self.host = config.redfish_host | ||
self.username = config.redfish_username | ||
self.password = config.redfish_password | ||
self.timeout = config.redfish_client_timeout | ||
self.max_retry = config.redfish_client_max_retry | ||
self.discover = self.get_discover(config.redfish_discover_cache_ttl) | ||
|
||
def get_sensor_data(self) -> Dict[str, List]: | ||
"""Get sensor data. | ||
Returns: | ||
sensor_data: a dictionary where key, value maps to chassis name, sensor data. | ||
""" | ||
data = self._get_sensor_data(host=host, username=username, password=password) | ||
if not data: | ||
return {} | ||
output = {} | ||
for chassis in data: | ||
name = str(chassis["ChassisName"]) | ||
sensors = [] | ||
for sensor in chassis["Readings"]: | ||
reading: str = str(sensor["Reading"]) + (sensor["Units"] or "") | ||
data = self._retrieve_redfish_sensor_data() | ||
return self._map_sensor_data_to_chassis(data) | ||
|
||
sensors.append( | ||
{ | ||
"Sensor": sensor["Name"], | ||
"Reading": reading, | ||
"Health": sensor["Health"] or "N/A", | ||
} | ||
) | ||
output[name] = sensors | ||
def _map_sensor_data_to_chassis(self, sensor_data: List[Any]) -> Dict[str, List]: | ||
output = {} | ||
for chassis in sensor_data: | ||
output[str(chassis["ChassisName"])] = [ | ||
{ | ||
"Sensor": sensor["Name"], | ||
"Reading": str(sensor["Reading"]) + (sensor["Units"] or ""), | ||
"Health": sensor["Health"] or "N/A", | ||
} | ||
for sensor in chassis["Readings"] | ||
] | ||
return output | ||
|
||
def _get_sensor_data(self, host: str, username: str, password: str) -> Optional[List[Any]]: | ||
"""Return sensor if sensor exists else None.""" | ||
sensors: Optional[List[Any]] = None | ||
redfish_obj: Optional[HttpClient] = None | ||
def _retrieve_redfish_sensor_data(self) -> List[Any]: | ||
"""Return sensor if sensor exists else None. | ||
Returns: | ||
sensors: List of dicts with details for each sensor | ||
""" | ||
try: | ||
redfish_obj = redfish.redfish_client( | ||
base_url=host, username=username, password=password | ||
) | ||
redfish_obj.login(auth="session") | ||
sensors = redfish_utilities.get_sensors(redfish_obj) | ||
return sensors | ||
except (InvalidCredentialsError, SessionCreationError, ConnectionError) as err: | ||
with redfish.redfish_client( | ||
base_url=self.host, | ||
username=self.username, | ||
password=self.password, | ||
timeout=self.timeout, | ||
max_retry=self.max_retry, | ||
) as redfish_obj: | ||
return redfish_utilities.get_sensors(redfish_obj) | ||
except ( | ||
InvalidCredentialsError, | ||
SessionCreationError, | ||
ConnectionError, | ||
RetriesExhaustedError, | ||
) as err: | ||
logger.exception(err) | ||
finally: | ||
# Log out | ||
if redfish_obj: | ||
redfish_obj.logout() | ||
return sensors | ||
return [] | ||
|
||
def get_discover(self, ttl: int) -> Callable: | ||
"""Return the cached discover function. | ||
Passes the ttl value to the cache decorator at runtime. | ||
""" | ||
|
||
@ttl_cache(ttl=ttl) | ||
def _discover() -> bool: | ||
"""Return true if redfish services have been discovered.""" | ||
logger.info("Discovering redfish services...") | ||
services = redfish.discover_ssdp() | ||
if len(services) == 0: | ||
logger.info("No redfish services discovered") | ||
return False | ||
logger.info("Discovered redfish services: %s", services) | ||
return True | ||
|
||
def discover(self) -> bool: | ||
"""Return true if redfish is been discovered.""" | ||
services = redfish.discover_ssdp() | ||
if len(services) == 0: | ||
return False | ||
return True | ||
return _discover |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
cachetools | ||
prometheus-client | ||
pydantic < 2.0 | ||
pyyaml | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
cachetools | ||
pytest | ||
freezegun |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.