pyghmi 1.5.71__py3-none-any.whl → 1.5.75__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pyghmi/ipmi/command.py CHANGED
@@ -551,7 +551,7 @@ class Command(object):
551
551
  return {'powerstate': self._get_power_state(
552
552
  bridge_request=bridge_request)}
553
553
 
554
- def set_identify(self, on=True, duration=None):
554
+ def set_identify(self, on=True, duration=None, blink=False):
555
555
  """Request identify light
556
556
 
557
557
  Request the identify light to turn off, on for a duration,
@@ -563,10 +563,14 @@ class Command(object):
563
563
  """
564
564
  self.oem_init()
565
565
  try:
566
- self._oem.set_identify(on, duration)
566
+ self._oem.set_identify(on, duration, blink)
567
+ return
568
+ except exc.BypassGenericBehavior:
567
569
  return
568
570
  except exc.UnsupportedFunctionality:
569
571
  pass
572
+ if blink:
573
+ raise exc.IpmiException('Blink not supported with generic IPMI')
570
574
  if duration is not None:
571
575
  duration = int(duration)
572
576
  if duration > 255:
@@ -104,6 +104,8 @@ class OEMHandler(object):
104
104
  if sensor.sensor_type != 'Temperature':
105
105
  continue
106
106
  if sensor.entity == 'External environment':
107
+ if 'exhaust' in sensor.sensor_name.lower():
108
+ continue
107
109
  extenv.append(sensor.sensor_name)
108
110
  if sensor.entity == 'Air inlet':
109
111
  airinlets.append(sensor.sensor_name)
@@ -377,7 +379,7 @@ class OEMHandler(object):
377
379
  def list_media(self):
378
380
  raise exc.UnsupportedFunctionality()
379
381
 
380
- def set_identify(self, on, duration):
382
+ def set_identify(self, on, duration, blink):
381
383
  """Provide an OEM override for set_identify
382
384
 
383
385
  Some systems may require an override for set identify.
@@ -671,9 +671,11 @@ class OEMHandler(generic.OEMHandler):
671
671
  led_status_default)
672
672
  yield (name, {'status': status})
673
673
 
674
- def set_identify(self, on, duration):
674
+ def set_identify(self, on, duration, blink):
675
675
  if on and not duration and self.is_sd350:
676
676
  self.ipmicmd.xraw_command(netfn=0x3a, command=6, data=(1, 1))
677
+ elif self.has_xcc:
678
+ self.immhandler.set_identify(on, duration, blink)
677
679
  else:
678
680
  raise pygexc.UnsupportedFunctionality()
679
681
 
@@ -968,6 +968,15 @@ class XCCClient(IMMClient):
968
968
  fru['manufacturer'] = memi['memory_manufacturer']
969
969
  break
970
970
 
971
+ def set_identify(self, on, duration, blink):
972
+ if blink:
973
+ self.grab_redfish_response_with_status(
974
+ '/redfish/v1/Systems/1',
975
+ {'IndicatorLED': 'Blinking'},
976
+ method='PATCH')
977
+ raise pygexc.BypassGenericBehavior()
978
+ raise pygexc.UnsupportedFunctionality()
979
+
971
980
  def get_description(self):
972
981
  dsc = self.wc.grab_json_response('/DeviceDescription.json')
973
982
  dsc = dsc[0]
pyghmi/redfish/command.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # coding: utf8
2
- # Copyright 2021 Lenovo
2
+ # Copyright 2025 Lenovo
3
3
  #
4
4
  # Licensed under the Apache License, Version 2.0 (the "License");
5
5
  # you may not use this file except in compliance with the License.
@@ -81,20 +81,6 @@ def _mask_to_cidr(mask):
81
81
  return cidr
82
82
 
83
83
 
84
- def _to_boolean(attrval):
85
- attrval = attrval.lower()
86
- if not attrval:
87
- return False
88
- if ('true'.startswith(attrval) or 'yes'.startswith(attrval)
89
- or 'enabled'.startswith(attrval) or attrval == '1'):
90
- return True
91
- if ('false'.startswith(attrval) or 'no'.startswith(attrval)
92
- or 'disabled'.startswith(attrval) or attrval == '0'):
93
- return False
94
- raise Exception(
95
- 'Unrecognized candidate for boolean: {0}'.format(attrval))
96
-
97
-
98
84
  def _cidr_to_mask(cidr):
99
85
  return socket.inet_ntop(
100
86
  socket.AF_INET, struct.pack(
@@ -127,6 +113,7 @@ def natural_sort(iterable):
127
113
  class SensorReading(object):
128
114
  def __init__(self, healthinfo, sensor=None, value=None, units=None,
129
115
  unavailable=False):
116
+ self.states = []
130
117
  if sensor:
131
118
  self.name = sensor['name']
132
119
  else:
@@ -136,7 +123,8 @@ class SensorReading(object):
136
123
  self.states = [healthinfo.get('Status', {}).get('Health',
137
124
  'Unknown')]
138
125
  self.health = _healthmap[healthinfo['Status']['Health']]
139
- self.states = [healthinfo['Status']['Health']]
126
+ if healthinfo['Status']['Health'].lower() == 'ok':
127
+ self.states = []
140
128
  self.value = value
141
129
  self.state_ids = None
142
130
  self.imprecision = None
@@ -175,6 +163,7 @@ class Command(object):
175
163
  self._gpool = pool
176
164
  self._bmcv4ip = None
177
165
  self._bmcv6ip = None
166
+ self.xauthtoken = None
178
167
  for addrinf in socket.getaddrinfo(bmc, 0, 0, socket.SOCK_STREAM):
179
168
  if addrinf[0] == socket.AF_INET:
180
169
  self._bmcv4ip = socket.inet_pton(addrinf[0], addrinf[-1][0])
@@ -187,38 +176,76 @@ class Command(object):
187
176
  self.wc.set_header('Accept-Encoding', 'gzip')
188
177
  self.wc.set_header('OData-Version', '4.0')
189
178
  overview = self.wc.grab_json_response('/redfish/v1/')
190
- self.wc.set_basic_credentials(userid, password)
191
179
  self.username = userid
192
180
  self.password = password
181
+ self.wc.set_basic_credentials(self.username, self.password)
193
182
  self.wc.set_header('Content-Type', 'application/json')
194
- if 'Systems' not in overview:
183
+ if 'Systems' not in overview and 'Managers' not in overview:
195
184
  raise exc.PyghmiException('Redfish not ready')
196
- systems = overview['Systems']['@odata.id']
197
- res = self.wc.grab_json_response_with_status(systems)
198
- if res[1] == 401:
199
- raise exc.PyghmiException('Access Denied')
200
- elif res[1] < 200 or res[1] >= 300:
201
- raise exc.PyghmiException(repr(res[0]))
202
- members = res[0]
185
+ if 'SessionService' in overview:
186
+ self._get_session_token(self.wc)
203
187
  self._varsensormap = {}
204
- systems = members['Members']
205
- if sysurl:
206
- for system in systems:
207
- if system['@odata.id'] == sysurl or system['@odata.id'].split('/')[-1] == sysurl:
208
- self.sysurl = system['@odata.id']
209
- break
188
+ self.powerurl = None
189
+ self.sysurl = None
190
+ if 'Managers' in overview:
191
+ bmcoll = systems = overview['Managers']['@odata.id']
192
+ res = self.wc.grab_json_response_with_status(bmcoll)
193
+ if res[1] == 401:
194
+ raise exc.PyghmiException('Access Denied')
195
+ elif res[1] < 200 or res[1] >= 300:
196
+ raise exc.PyghmiException(repr(res[0]))
197
+ bmcs = res[0]['Members']
198
+ if len(bmcs) == 1:
199
+ self._varbmcurl = bmcs[0]['@odata.id']
200
+ if 'Systems' in overview:
201
+ systems = overview['Systems']['@odata.id']
202
+ res = self.wc.grab_json_response_with_status(systems)
203
+ if res[1] == 401:
204
+ raise exc.PyghmiException('Access Denied')
205
+ elif res[1] < 200 or res[1] >= 300:
206
+ raise exc.PyghmiException(repr(res[0]))
207
+ members = res[0]
208
+ systems = members['Members']
209
+ if sysurl:
210
+ for system in systems:
211
+ if system['@odata.id'] == sysurl or system['@odata.id'].split('/')[-1] == sysurl:
212
+ self.sysurl = system['@odata.id']
213
+ break
214
+ else:
215
+ raise exc.PyghmiException(
216
+ 'Specified sysurl not found: {0}'.format(sysurl))
210
217
  else:
211
- raise exc.PyghmiException(
212
- 'Specified sysurl not found: {0}'.format(sysurl))
213
- else:
214
- if len(systems) != 1:
215
- systems = [x for x in systems if 'DPU' not in x['@odata.id']]
216
- if len(systems) != 1:
217
- raise exc.PyghmiException(
218
- 'Multi system manager, sysurl is required parameter')
219
- self.sysurl = systems[0]['@odata.id']
220
- self.powerurl = self.sysinfo.get('Actions', {}).get(
221
- '#ComputerSystem.Reset', {}).get('target', None)
218
+ if len(systems) > 1:
219
+ systems = [x for x in systems if 'DPU' not in x['@odata.id']]
220
+ if len(systems) > 1:
221
+ raise exc.PyghmiException(
222
+ 'Multi system manager, sysurl is required parameter')
223
+ if len(systems):
224
+ self.sysurl = systems[0]['@odata.id']
225
+ else:
226
+ self.sysurl = None
227
+ self.powerurl = self.sysinfo.get('Actions', {}).get(
228
+ '#ComputerSystem.Reset', {}).get('target', None)
229
+
230
+ def _get_session_token(self, wc):
231
+ # specification actually indicates we can skip straight to this url
232
+ username = self.username
233
+ password = self.password
234
+ if not isinstance(username, str):
235
+ username = username.decode()
236
+ if not isinstance(password, str):
237
+ password = password.decode()
238
+ rsp = wc.grab_rsp('/redfish/v1/SessionService/Sessions',
239
+ {'UserName': username, 'Password': password})
240
+ rsp.read()
241
+ self.xauthtoken = rsp.getheader('X-Auth-Token')
242
+ if self.xauthtoken:
243
+ if 'Authorization' in wc.stdheaders:
244
+ del wc.stdheaders['Authorization']
245
+ if 'Authorization' in self.wc.stdheaders:
246
+ del self.wc.stdheaders['Authorization']
247
+ wc.stdheaders['X-Auth-Token'] = self.xauthtoken
248
+ self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken
222
249
 
223
250
  @property
224
251
  def _accountserviceurl(self):
@@ -445,7 +472,13 @@ class Command(object):
445
472
 
446
473
  @property
447
474
  def sysinfo(self):
448
- return self._do_web_request(self.sysurl)
475
+ if not self.sysurl:
476
+ return {}
477
+ try:
478
+ return self._do_web_request(self.sysurl)
479
+ except exc.RedfishError:
480
+ self.sysurl = None
481
+ return {}
449
482
 
450
483
  @property
451
484
  def bmcinfo(self):
@@ -534,6 +567,17 @@ class Command(object):
534
567
  finally:
535
568
  if 'If-Match' in wc.stdheaders:
536
569
  del wc.stdheaders['If-Match']
570
+ if res[1] == 401 and self.xauthtoken:
571
+ wc.set_basic_credentials(self.username, self.password)
572
+ self._get_session_token(wc)
573
+ if etag:
574
+ wc.stdheaders['If-Match'] = etag
575
+ try:
576
+ res = wc.grab_json_response_with_status(url, payload,
577
+ method=method)
578
+ finally:
579
+ if 'If-Match' in wc.stdheaders:
580
+ del wc.stdheaders['If-Match']
537
581
  if res[1] < 200 or res[1] >= 300:
538
582
  try:
539
583
  info = json.loads(res[0])
@@ -547,7 +591,7 @@ class Command(object):
547
591
  msgid = ','.join(msgid)
548
592
  raise exc.RedfishError(errmsg, msgid=msgid)
549
593
  except (ValueError, KeyError):
550
- raise exc.PyghmiException(str(url) + ":" + res[0])
594
+ raise exc.PyghmiException(str(url) + ":" + str(res[0]))
551
595
  if payload is None and method is None:
552
596
  self._urlcache[url] = {'contents': res[0],
553
597
  'vintage': os.times()[4]}
@@ -633,34 +677,53 @@ class Command(object):
633
677
  @property
634
678
  def _sensormap(self):
635
679
  if not self._varsensormap:
636
- for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
637
- self._mapchassissensors(chassis)
680
+ if self.sysinfo:
681
+ for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
682
+ self._mapchassissensors(chassis)
683
+ else: # no system, but check if this is a singular chassis
684
+ rootinfo = self._do_web_request('/redfish/v1/')
685
+ chassiscol = rootinfo.get('Chassis', {}).get('@odata.id', '')
686
+ if chassiscol:
687
+ chassislist = self._do_web_request(chassiscol)
688
+ if len(chassislist.get('Members', [])) == 1:
689
+ self._mapchassissensors(chassislist['Members'][0])
638
690
  return self._varsensormap
639
691
 
640
692
  def _mapchassissensors(self, chassis):
641
693
  chassisurl = chassis['@odata.id']
642
694
  chassisinfo = self._do_web_request(chassisurl)
643
- powurl = chassisinfo.get('Power', {}).get('@odata.id', '')
644
- if powurl:
645
- powinf = self._do_web_request(powurl)
646
- for voltage in powinf.get('Voltages', []):
647
- if 'Name' in voltage:
648
- self._varsensormap[voltage['Name']] = {
649
- 'name': voltage['Name'], 'url': powurl,
650
- 'type': 'Voltage'}
651
- thermurl = chassisinfo.get('Thermal', {}).get('@odata.id', '')
652
- if thermurl:
653
- therminf = self._do_web_request(thermurl)
654
- for fan in therminf.get('Fans', []):
655
- if 'Name' in fan:
656
- self._varsensormap[fan['Name']] = {
657
- 'name': fan['Name'], 'type': 'Fan',
658
- 'url': thermurl}
659
- for temp in therminf.get('Temperatures', []):
660
- if 'Name' in temp:
661
- self._varsensormap[temp['Name']] = {
662
- 'name': temp['Name'], 'type': 'Temperature',
663
- 'url': thermurl}
695
+ sensors = chassisinfo.get('Sensors', {}).get('@odata.id', '')
696
+ if sensors:
697
+ sensorinf = self._do_web_request(sensors)
698
+ for sensor in sensorinf.get('Members', []):
699
+ sensedata = self._do_web_request(sensor['@odata.id'])
700
+ if 'Name' in sensedata:
701
+ sensetype = sensedata.get('ReadingType', 'Unknown')
702
+ self._varsensormap[sensedata['Name']] = {
703
+ 'name': sensedata['Name'], 'type': sensetype,
704
+ 'url': sensor['@odata.id'], 'generic': True}
705
+ else:
706
+ powurl = chassisinfo.get('Power', {}).get('@odata.id', '')
707
+ if powurl:
708
+ powinf = self._do_web_request(powurl)
709
+ for voltage in powinf.get('Voltages', []):
710
+ if 'Name' in voltage:
711
+ self._varsensormap[voltage['Name']] = {
712
+ 'name': voltage['Name'], 'url': powurl,
713
+ 'type': 'Voltage'}
714
+ thermurl = chassisinfo.get('Thermal', {}).get('@odata.id', '')
715
+ if thermurl:
716
+ therminf = self._do_web_request(thermurl)
717
+ for fan in therminf.get('Fans', []):
718
+ if 'Name' in fan:
719
+ self._varsensormap[fan['Name']] = {
720
+ 'name': fan['Name'], 'type': 'Fan',
721
+ 'url': thermurl}
722
+ for temp in therminf.get('Temperatures', []):
723
+ if 'Name' in temp:
724
+ self._varsensormap[temp['Name']] = {
725
+ 'name': temp['Name'], 'type': 'Temperature',
726
+ 'url': thermurl}
664
727
  for subchassis in chassisinfo.get('Links', {}).get('Contains', []):
665
728
  self._mapchassissensors(subchassis)
666
729
 
@@ -771,8 +834,18 @@ class Command(object):
771
834
  self._do_web_request(url, {'ResetType': action})
772
835
 
773
836
  def set_identify(self, on=True, blink=None):
837
+ targurl = self.sysurl
838
+ if not targurl:
839
+ root = self._do_web_request('/redfish/v1')
840
+ systemsurl = root.get('Systems', {}).get('@odata.id', None)
841
+ if systemsurl:
842
+ targurl = self._do_web_request(systemsurl)
843
+ if len(targurl.get('Members', [])) == 1:
844
+ targurl = targurl['Members'][0]['@odata.id']
845
+ if not targurl:
846
+ raise Exception("Unable to identify system url")
774
847
  self._do_web_request(
775
- self.sysurl,
848
+ targurl,
776
849
  {'IndicatorLED': 'Blinking' if blink else 'Lit' if on else 'Off'},
777
850
  method='PATCH', etag='*')
778
851
 
@@ -819,6 +892,54 @@ class Command(object):
819
892
  def set_system_configuration(self, changeset):
820
893
  return self.oem.set_system_configuration(changeset, self)
821
894
 
895
+ def get_ntp_enabled(self):
896
+ bmcinfo = self._do_web_request(self._bmcurl)
897
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
898
+ if netprotocols:
899
+ netprotoinfo = self._do_web_request(netprotocols)
900
+ enabled = netprotoinfo.get('NTP', {}).get('ProtocolEnabled', False)
901
+ return enabled
902
+ return False
903
+
904
+ def set_ntp_enabled(self, enable):
905
+ bmcinfo = self._do_web_request(self._bmcurl)
906
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
907
+ if netprotocols:
908
+ request = {'NTP':{'ProtocolEnabled': enable}}
909
+ self._do_web_request(netprotocols, request, method='PATCH')
910
+ self._do_web_request(netprotocols, cache=0)
911
+
912
+ def get_ntp_servers(self):
913
+ bmcinfo = self._do_web_request(self._bmcurl)
914
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
915
+ if not netprotocols:
916
+ return []
917
+ netprotoinfo = self._do_web_request(netprotocols)
918
+ return netprotoinfo.get('NTP', {}).get('NTPServers', [])
919
+
920
+ def set_ntp_server(self, server, index=None):
921
+ bmcinfo = self._do_web_request(self._bmcurl)
922
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
923
+ currntpservers = self.get_ntp_servers()
924
+ if index is None:
925
+ if server in currntpservers:
926
+ return
927
+ currntpservers = [server] + currntpservers
928
+ else:
929
+ if (index + 1) > len(currntpservers):
930
+ if not server:
931
+ return
932
+ currntpservers.append(server)
933
+ else:
934
+ if not server:
935
+ del currntpservers[index]
936
+ else:
937
+ currntpservers[index] = server
938
+ request = {'NTP':{'NTPServers': currntpservers}}
939
+ self._do_web_request(netprotocols, request, method='PATCH')
940
+ self._do_web_request(netprotocols, cache=0)
941
+
942
+
822
943
  def clear_bmc_configuration(self):
823
944
  """Reset BMC to factory default
824
945
 
@@ -830,7 +951,7 @@ class Command(object):
830
951
  rc = bmcinfo.get('Actions', {}).get('#Manager.ResetToDefaults', {})
831
952
  actinf = rc.get('ResetType@Redfish.AllowableValues', [])
832
953
  if 'ResetAll' in actinf:
833
- acturl = actinf.get('target', None)
954
+ acturl = rc.get('target', None)
834
955
  if acturl:
835
956
  self._do_web_request(acturl, {'ResetType': 'ResetAll'})
836
957
  return
@@ -976,14 +1097,14 @@ class Command(object):
976
1097
  {'HostName': hostname}, 'PATCH')
977
1098
 
978
1099
  def get_firmware(self, components=()):
1100
+ self._fwnamemap = {}
979
1101
  try:
980
- for firminfo in self.oem.get_firmware_inventory(components):
1102
+ for firminfo in self.oem.get_firmware_inventory(components, self):
981
1103
  yield firminfo
982
1104
  except exc.BypassGenericBehavior:
983
1105
  return
984
1106
  fwlist = self._do_web_request(self._fwinventory)
985
1107
  fwurls = [x['@odata.id'] for x in fwlist.get('Members', [])]
986
- self._fwnamemap = {}
987
1108
  for res in self._do_bulk_requests(fwurls):
988
1109
  res = self._extract_fwinfo(res)
989
1110
  if res[0] is None:
@@ -1086,79 +1207,20 @@ class Command(object):
1086
1207
  @property
1087
1208
  def oem(self):
1088
1209
  if not self._oem:
1210
+ if self.sysurl:
1211
+ self._do_web_request(self.sysurl, cache=False) # This is to trigger token validation and renewel
1212
+ elif self._varbmcurl:
1213
+ self._do_web_request(self._varbmcurl, cache=False) # This is to trigger token validation and renewel
1089
1214
  self._oem = oem.get_oem_handler(
1090
1215
  self.sysinfo, self.sysurl, self.wc, self._urlcache, self)
1091
1216
  self._oem.set_credentials(self.username, self.password)
1092
1217
  return self._oem
1093
1218
 
1094
1219
  def get_description(self):
1095
- return self.oem.get_description()
1220
+ return self.oem.get_description(self)
1096
1221
 
1097
1222
  def get_event_log(self, clear=False):
1098
- bmcinfo = self._do_web_request(self._bmcurl)
1099
- lsurl = bmcinfo.get('LogServices', {}).get('@odata.id', None)
1100
- if not lsurl:
1101
- return
1102
- currtime = bmcinfo.get('DateTime', None)
1103
- correction = timedelta(0)
1104
- utz = tz.tzoffset('', 0)
1105
- ltz = tz.gettz()
1106
- if currtime:
1107
- currtime = parse_time(currtime)
1108
- if currtime:
1109
- now = datetime.now(utz)
1110
- try:
1111
- correction = now - currtime
1112
- except TypeError:
1113
- correction = now - currtime.replace(tzinfo=utz)
1114
- lurls = self._do_web_request(lsurl).get('Members', [])
1115
- for lurl in lurls:
1116
- lurl = lurl['@odata.id']
1117
- loginfo = self._do_web_request(lurl, cache=(not clear))
1118
- entriesurl = loginfo.get('Entries', {}).get('@odata.id', None)
1119
- if not entriesurl:
1120
- continue
1121
- logid = loginfo.get('Id', '')
1122
- entries = self._do_web_request(entriesurl, cache=False)
1123
- if clear:
1124
- # The clear is against the log service etag, not entries
1125
- # so we have to fetch service etag after we fetch entries
1126
- # until we can verify that the etag is consistent to prove
1127
- # that the clear is atomic
1128
- newloginfo = self._do_web_request(lurl, cache=False)
1129
- clearurl = newloginfo.get('Actions', {}).get(
1130
- '#LogService.ClearLog', {}).get('target', '')
1131
- while clearurl:
1132
- try:
1133
- self._do_web_request(clearurl, method='POST',
1134
- payload={})
1135
- clearurl = False
1136
- except exc.PyghmiException as e:
1137
- if 'EtagPreconditionalFailed' not in str(e):
1138
- raise
1139
- # This doesn't guarantee atomicity, but it mitigates
1140
- # greatly. Unfortunately some implementations
1141
- # mutate the tag endlessly and we have no hope
1142
- entries = self._do_web_request(entriesurl, cache=False)
1143
- newloginfo = self._do_web_request(lurl, cache=False)
1144
- for log in entries.get('Members', []):
1145
- if ('Created' not in log and 'Message' not in log
1146
- and 'Severity' not in log):
1147
- # without any data, this log entry isn't actionable
1148
- continue
1149
- record = {}
1150
- record['log_id'] = logid
1151
- parsedtime = parse_time(log.get('Created', ''))
1152
- if parsedtime:
1153
- entime = parsedtime + correction
1154
- entime = entime.astimezone(ltz)
1155
- record['timestamp'] = entime.strftime('%Y-%m-%dT%H:%M:%S')
1156
- else:
1157
- record['timestamp'] = log.get('Created', '')
1158
- record['message'] = log.get('Message', None)
1159
- record['severity'] = _healthmap.get(
1160
- log.get('Severity', 'Warning'), const.Health.Ok)
1161
- yield record
1223
+ return self.oem.get_event_log(clear, self)
1162
1224
 
1163
1225
  def _get_chassis_env(self, chassis):
1164
1226
  chassisurl = chassis['@odata.id']
@@ -1174,35 +1236,11 @@ class Command(object):
1174
1236
  return retval
1175
1237
 
1176
1238
  def get_average_processor_temperature(self):
1177
- cputemps = []
1178
- for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1179
- thermals = self._get_thermals(chassis)
1180
- for temp in thermals:
1181
- if temp.get('PhysicalContext', '') != 'CPU':
1182
- continue
1183
- if temp.get('ReadingCelsius', None) is None:
1184
- continue
1185
- cputemps.append(temp['ReadingCelsius'])
1186
- if not cputemps:
1187
- return SensorReading(
1188
- None, {'name': 'Average Processor Temperature'}, value=None, units='°C',
1189
- unavailable=True)
1190
- avgtemp = sum(cputemps) / len(cputemps)
1191
- return SensorReading(
1192
- None, {'name': 'Average Processor Temperature'}, value=avgtemp, units='°C')
1239
+ return self.oem.get_average_processor_temperature(self)
1240
+
1193
1241
 
1194
1242
  def get_system_power_watts(self):
1195
- totalwatts = 0
1196
- gotpower = False
1197
- for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1198
- envinfo = self._get_chassis_env(chassis)
1199
- currwatts = envinfo.get('watts', None)
1200
- if currwatts is not None:
1201
- gotpower = True
1202
- totalwatts += envinfo['watts']
1203
- if not gotpower:
1204
- raise exc.UnsupportedFunctionality("System does not provide Power under redfish EnvironmentMetrics")
1205
- return totalwatts
1243
+ return self.oem.get_system_power_watts(self)
1206
1244
 
1207
1245
  def get_inlet_temperature(self):
1208
1246
  inlets = []
@@ -1236,6 +1274,16 @@ class Command(object):
1236
1274
  yield self.get_sensor_reading(sensor)
1237
1275
 
1238
1276
  def _extract_reading(self, sensor, reading):
1277
+ if sensor.get('generic', False): # generic sensor
1278
+ val = reading.get('Reading', None)
1279
+ unavail = val is None
1280
+ units = reading.get('ReadingUnits', None)
1281
+ if units == 'Cel':
1282
+ units = '°C'
1283
+ if units == 'cft_i/min':
1284
+ units = 'CFM'
1285
+ return SensorReading(reading, None, value=val, units=units,
1286
+ unavailable=unavail)
1239
1287
  if sensor['type'] == 'Fan':
1240
1288
  for fan in reading['Fans']:
1241
1289
  if fan['Name'] == sensor['name']:
@@ -1444,3 +1492,4 @@ if __name__ == '__main__':
1444
1492
  print(repr(
1445
1493
  Command(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'],
1446
1494
  verifycallback=lambda x: True).get_power()))
1495
+