From 3d4c5c7811fcdfe52b210b6d94b66b5e985faae9 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Thu, 26 Oct 2023 08:24:56 -0400 Subject: [PATCH] Add normalized temperature/power data for redfish As has been done for IPMI, create hooks for popular sensor metrics that users want normalized across distinct models. This allows a client to request pertinent data without having to keep track of inconsistent sensor names across different systems. Change-Id: Idd1ac8222231f1a786a4f4af148e3fc3d1f6c89b --- pyghmi/ipmi/oem/generic.py | 4 ++- pyghmi/redfish/command.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/pyghmi/ipmi/oem/generic.py b/pyghmi/ipmi/oem/generic.py index af89bf89..d5747353 100644 --- a/pyghmi/ipmi/oem/generic.py +++ b/pyghmi/ipmi/oem/generic.py @@ -74,10 +74,12 @@ class OEMHandler(object): except exc.IpmiException: continue tmplreading = reading - readingvalues.append(reading.value) + if reading.value is not None: + readingvalues.append(float(reading.value)) avgval = sum(readingvalues) / len(readingvalues) tmplreading.name = 'Average Processor Temperature' tmplreading.value = avgval + tmplreading.unavailable = 0 return tmplreading diff --git a/pyghmi/redfish/command.py b/pyghmi/redfish/command.py index 4848df2a..7c606679 100644 --- a/pyghmi/redfish/command.py +++ b/pyghmi/redfish/command.py @@ -142,6 +142,16 @@ class SensorReading(object): self.imprecision = None self.units = units self.unavailable = unavailable + + def __repr__(self): + return repr({ + 'value': self.value, + 'state_ids': self.state_ids, + 'units': self.units, + 'imprecision': self.imprecision, + 'name': self.name, + 'unavailable': self.unavailable, + }) class Command(object): @@ -509,6 +519,8 @@ class Command(object): def _do_web_request(self, url, payload=None, method=None, cache=True, etag=None): res = None + if cache is True: + cache = 30 if cache and payload is None and method is None: res = self._get_cache(url, cache) if res: @@ -652,6 +664,14 @@ class Command(object): for subchassis in chassisinfo.get('Links', {}).get('Contains', []): self._mapchassissensors(subchassis) + def _get_thermals(self, chassis): + chassisurl = chassis['@odata.id'] + chassisinfo = self._do_web_request(chassisurl) + thermurl = chassisinfo.get('Thermal', {}).get('@odata.id', '') + if thermurl: + therminf = self._do_web_request(thermurl, cache=1) + return therminf.get('Temperatures', []) + @property def _bmcurl(self): if not self._varbmcurl: @@ -1129,6 +1149,46 @@ class Command(object): log.get('Severity', 'Warning'), const.Health.Ok) yield record + def _get_chassis_env(self, chassis): + chassisurl = chassis['@odata.id'] + chassisinfo = self._do_web_request(chassisurl) + envurl = chassisinfo.get('EnvironmentMetrics', {}).get('@odata.id', '') + envmetric = self._do_web_request(envurl, cache=1) + retval = { + 'watts': envmetric.get('PowerWatts', {}).get('Reading', None), + 'inlet': envmetric.get('TemperatureCelsius', {}).get('Reading', None) + } + return retval + + def get_average_processor_temperature(self): + cputemps = [] + for chassis in self.sysinfo.get('Links', {}).get('Chassis', []): + thermals = self._get_thermals(chassis) + for temp in thermals: + if temp.get('PhysicalContext', '') != 'CPU': + continue + if temp.get('ReadingCelsius', None) is None: + continue + cputemps.append(temp['ReadingCelsius']) + avgtemp = sum(cputemps) / len(cputemps) + return SensorReading( + None, {'name': 'Average Processor Temperature'}, value=avgtemp, units='°C') + + def get_system_power_watts(self): + totalwatts = 0 + for chassis in self.sysinfo.get('Links', {}).get('Chassis', []): + envinfo = self._get_chassis_env(chassis) + totalwatts += envinfo['watts'] + return totalwatts + + def get_inlet_temperature(self): + inlets = [] + for chassis in self.sysinfo.get('Links', {}).get('Chassis', []): + envinfo = self._get_chassis_env(chassis) + inlets.append(envinfo['inlet']) + val = sum(inlets) / len(inlets) + return SensorReading( + None, {'name': 'Inlet Temperature'}, value=val, units='°C') def get_sensor_descriptions(self): for sensor in natural_sort(self._sensormap): yield self._sensormap[sensor]