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

Sign up to get free protection for your applications and to get access to all the features.
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
+