pyghmi 1.5.72__py3-none-any.whl → 1.5.76__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.
@@ -68,6 +68,7 @@ _healthmap = {
68
68
  'Unknown': const.Health.Warning,
69
69
  'Warning': const.Health.Warning,
70
70
  'OK': const.Health.Ok,
71
+ None: const.Health.Ok,
71
72
  }
72
73
 
73
74
 
@@ -113,6 +114,7 @@ def natural_sort(iterable):
113
114
  class SensorReading(object):
114
115
  def __init__(self, healthinfo, sensor=None, value=None, units=None,
115
116
  unavailable=False):
117
+ self.states = []
116
118
  if sensor:
117
119
  self.name = sensor['name']
118
120
  else:
@@ -122,7 +124,8 @@ class SensorReading(object):
122
124
  self.states = [healthinfo.get('Status', {}).get('Health',
123
125
  'Unknown')]
124
126
  self.health = _healthmap[healthinfo['Status']['Health']]
125
- self.states = [healthinfo['Status']['Health']]
127
+ if self.health == const.Health.Ok:
128
+ self.states = []
126
129
  self.value = value
127
130
  self.state_ids = None
128
131
  self.imprecision = None
@@ -161,6 +164,7 @@ class Command(object):
161
164
  self._gpool = pool
162
165
  self._bmcv4ip = None
163
166
  self._bmcv6ip = None
167
+ self.xauthtoken = None
164
168
  for addrinf in socket.getaddrinfo(bmc, 0, 0, socket.SOCK_STREAM):
165
169
  if addrinf[0] == socket.AF_INET:
166
170
  self._bmcv4ip = socket.inet_pton(addrinf[0], addrinf[-1][0])
@@ -173,38 +177,76 @@ class Command(object):
173
177
  self.wc.set_header('Accept-Encoding', 'gzip')
174
178
  self.wc.set_header('OData-Version', '4.0')
175
179
  overview = self.wc.grab_json_response('/redfish/v1/')
176
- self.wc.set_basic_credentials(userid, password)
177
180
  self.username = userid
178
181
  self.password = password
182
+ self.wc.set_basic_credentials(self.username, self.password)
179
183
  self.wc.set_header('Content-Type', 'application/json')
180
- if 'Systems' not in overview:
184
+ if 'Systems' not in overview and 'Managers' not in overview:
181
185
  raise exc.PyghmiException('Redfish not ready')
182
- systems = overview['Systems']['@odata.id']
183
- res = self.wc.grab_json_response_with_status(systems)
184
- if res[1] == 401:
185
- raise exc.PyghmiException('Access Denied')
186
- elif res[1] < 200 or res[1] >= 300:
187
- raise exc.PyghmiException(repr(res[0]))
188
- members = res[0]
186
+ if 'SessionService' in overview:
187
+ self._get_session_token(self.wc)
189
188
  self._varsensormap = {}
190
- systems = members['Members']
191
- if sysurl:
192
- for system in systems:
193
- if system['@odata.id'] == sysurl or system['@odata.id'].split('/')[-1] == sysurl:
194
- self.sysurl = system['@odata.id']
195
- break
189
+ self.powerurl = None
190
+ self.sysurl = None
191
+ if 'Managers' in overview:
192
+ bmcoll = systems = overview['Managers']['@odata.id']
193
+ res = self.wc.grab_json_response_with_status(bmcoll)
194
+ if res[1] == 401:
195
+ raise exc.PyghmiException('Access Denied')
196
+ elif res[1] < 200 or res[1] >= 300:
197
+ raise exc.PyghmiException(repr(res[0]))
198
+ bmcs = res[0]['Members']
199
+ if len(bmcs) == 1:
200
+ self._varbmcurl = bmcs[0]['@odata.id']
201
+ if 'Systems' in overview:
202
+ systems = overview['Systems']['@odata.id']
203
+ res = self.wc.grab_json_response_with_status(systems)
204
+ if res[1] == 401:
205
+ raise exc.PyghmiException('Access Denied')
206
+ elif res[1] < 200 or res[1] >= 300:
207
+ raise exc.PyghmiException(repr(res[0]))
208
+ members = res[0]
209
+ systems = members['Members']
210
+ if sysurl:
211
+ for system in systems:
212
+ if system['@odata.id'] == sysurl or system['@odata.id'].split('/')[-1] == sysurl:
213
+ self.sysurl = system['@odata.id']
214
+ break
215
+ else:
216
+ raise exc.PyghmiException(
217
+ 'Specified sysurl not found: {0}'.format(sysurl))
196
218
  else:
197
- raise exc.PyghmiException(
198
- 'Specified sysurl not found: {0}'.format(sysurl))
199
- else:
200
- if len(systems) != 1:
201
- systems = [x for x in systems if 'DPU' not in x['@odata.id']]
202
- if len(systems) != 1:
203
- raise exc.PyghmiException(
204
- 'Multi system manager, sysurl is required parameter')
205
- self.sysurl = systems[0]['@odata.id']
206
- self.powerurl = self.sysinfo.get('Actions', {}).get(
207
- '#ComputerSystem.Reset', {}).get('target', None)
219
+ if len(systems) > 1:
220
+ systems = [x for x in systems if 'DPU' not in x['@odata.id']]
221
+ if len(systems) > 1:
222
+ raise exc.PyghmiException(
223
+ 'Multi system manager, sysurl is required parameter')
224
+ if len(systems):
225
+ self.sysurl = systems[0]['@odata.id']
226
+ else:
227
+ self.sysurl = None
228
+ self.powerurl = self.sysinfo.get('Actions', {}).get(
229
+ '#ComputerSystem.Reset', {}).get('target', None)
230
+
231
+ def _get_session_token(self, wc):
232
+ # specification actually indicates we can skip straight to this url
233
+ username = self.username
234
+ password = self.password
235
+ if not isinstance(username, str):
236
+ username = username.decode()
237
+ if not isinstance(password, str):
238
+ password = password.decode()
239
+ rsp = wc.grab_rsp('/redfish/v1/SessionService/Sessions',
240
+ {'UserName': username, 'Password': password})
241
+ rsp.read()
242
+ self.xauthtoken = rsp.getheader('X-Auth-Token')
243
+ if self.xauthtoken:
244
+ if 'Authorization' in wc.stdheaders:
245
+ del wc.stdheaders['Authorization']
246
+ if 'Authorization' in self.wc.stdheaders:
247
+ del self.wc.stdheaders['Authorization']
248
+ wc.stdheaders['X-Auth-Token'] = self.xauthtoken
249
+ self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken
208
250
 
209
251
  @property
210
252
  def _accountserviceurl(self):
@@ -431,7 +473,13 @@ class Command(object):
431
473
 
432
474
  @property
433
475
  def sysinfo(self):
434
- return self._do_web_request(self.sysurl)
476
+ if not self.sysurl:
477
+ return {}
478
+ try:
479
+ return self._do_web_request(self.sysurl)
480
+ except exc.RedfishError:
481
+ self.sysurl = None
482
+ return {}
435
483
 
436
484
  @property
437
485
  def bmcinfo(self):
@@ -520,6 +568,17 @@ class Command(object):
520
568
  finally:
521
569
  if 'If-Match' in wc.stdheaders:
522
570
  del wc.stdheaders['If-Match']
571
+ if res[1] == 401 and self.xauthtoken:
572
+ wc.set_basic_credentials(self.username, self.password)
573
+ self._get_session_token(wc)
574
+ if etag:
575
+ wc.stdheaders['If-Match'] = etag
576
+ try:
577
+ res = wc.grab_json_response_with_status(url, payload,
578
+ method=method)
579
+ finally:
580
+ if 'If-Match' in wc.stdheaders:
581
+ del wc.stdheaders['If-Match']
523
582
  if res[1] < 200 or res[1] >= 300:
524
583
  try:
525
584
  info = json.loads(res[0])
@@ -619,34 +678,55 @@ class Command(object):
619
678
  @property
620
679
  def _sensormap(self):
621
680
  if not self._varsensormap:
622
- for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
623
- self._mapchassissensors(chassis)
681
+ if self.sysinfo:
682
+ for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
683
+ self._mapchassissensors(chassis)
684
+ else: # no system, but check if this is a singular chassis
685
+ rootinfo = self._do_web_request('/redfish/v1/')
686
+ chassiscol = rootinfo.get('Chassis', {}).get('@odata.id', '')
687
+ if chassiscol:
688
+ chassislist = self._do_web_request(chassiscol)
689
+ if len(chassislist.get('Members', [])) == 1:
690
+ self._mapchassissensors(chassislist['Members'][0])
624
691
  return self._varsensormap
625
692
 
626
693
  def _mapchassissensors(self, chassis):
627
694
  chassisurl = chassis['@odata.id']
628
695
  chassisinfo = self._do_web_request(chassisurl)
629
- powurl = chassisinfo.get('Power', {}).get('@odata.id', '')
630
- if powurl:
631
- powinf = self._do_web_request(powurl)
632
- for voltage in powinf.get('Voltages', []):
633
- if 'Name' in voltage:
634
- self._varsensormap[voltage['Name']] = {
635
- 'name': voltage['Name'], 'url': powurl,
636
- 'type': 'Voltage'}
637
- thermurl = chassisinfo.get('Thermal', {}).get('@odata.id', '')
638
- if thermurl:
639
- therminf = self._do_web_request(thermurl)
640
- for fan in therminf.get('Fans', []):
641
- if 'Name' in fan:
642
- self._varsensormap[fan['Name']] = {
643
- 'name': fan['Name'], 'type': 'Fan',
644
- 'url': thermurl}
645
- for temp in therminf.get('Temperatures', []):
646
- if 'Name' in temp:
647
- self._varsensormap[temp['Name']] = {
648
- 'name': temp['Name'], 'type': 'Temperature',
649
- 'url': thermurl}
696
+ sensors = None
697
+ if self.oem.usegenericsensors:
698
+ sensors = chassisinfo.get('Sensors', {}).get('@odata.id', '')
699
+ if sensors:
700
+ sensorinf = self._do_web_request(sensors)
701
+ for sensor in sensorinf.get('Members', []):
702
+ sensedata = self._do_web_request(sensor['@odata.id'])
703
+ if 'Name' in sensedata:
704
+ sensetype = sensedata.get('ReadingType', 'Unknown')
705
+ self._varsensormap[sensedata['Name']] = {
706
+ 'name': sensedata['Name'], 'type': sensetype,
707
+ 'url': sensor['@odata.id'], 'generic': True}
708
+ else:
709
+ powurl = chassisinfo.get('Power', {}).get('@odata.id', '')
710
+ if powurl:
711
+ powinf = self._do_web_request(powurl)
712
+ for voltage in powinf.get('Voltages', []):
713
+ if 'Name' in voltage:
714
+ self._varsensormap[voltage['Name']] = {
715
+ 'name': voltage['Name'], 'url': powurl,
716
+ 'type': 'Voltage'}
717
+ thermurl = chassisinfo.get('Thermal', {}).get('@odata.id', '')
718
+ if thermurl:
719
+ therminf = self._do_web_request(thermurl)
720
+ for fan in therminf.get('Fans', []):
721
+ if 'Name' in fan:
722
+ self._varsensormap[fan['Name']] = {
723
+ 'name': fan['Name'], 'type': 'Fan',
724
+ 'url': thermurl}
725
+ for temp in therminf.get('Temperatures', []):
726
+ if 'Name' in temp:
727
+ self._varsensormap[temp['Name']] = {
728
+ 'name': temp['Name'], 'type': 'Temperature',
729
+ 'url': thermurl}
650
730
  for subchassis in chassisinfo.get('Links', {}).get('Contains', []):
651
731
  self._mapchassissensors(subchassis)
652
732
 
@@ -757,8 +837,18 @@ class Command(object):
757
837
  self._do_web_request(url, {'ResetType': action})
758
838
 
759
839
  def set_identify(self, on=True, blink=None):
840
+ targurl = self.sysurl
841
+ if not targurl:
842
+ root = self._do_web_request('/redfish/v1')
843
+ systemsurl = root.get('Systems', {}).get('@odata.id', None)
844
+ if systemsurl:
845
+ targurl = self._do_web_request(systemsurl)
846
+ if len(targurl.get('Members', [])) == 1:
847
+ targurl = targurl['Members'][0]['@odata.id']
848
+ if not targurl:
849
+ raise Exception("Unable to identify system url")
760
850
  self._do_web_request(
761
- self.sysurl,
851
+ targurl,
762
852
  {'IndicatorLED': 'Blinking' if blink else 'Lit' if on else 'Off'},
763
853
  method='PATCH', etag='*')
764
854
 
@@ -805,6 +895,54 @@ class Command(object):
805
895
  def set_system_configuration(self, changeset):
806
896
  return self.oem.set_system_configuration(changeset, self)
807
897
 
898
+ def get_ntp_enabled(self):
899
+ bmcinfo = self._do_web_request(self._bmcurl)
900
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
901
+ if netprotocols:
902
+ netprotoinfo = self._do_web_request(netprotocols)
903
+ enabled = netprotoinfo.get('NTP', {}).get('ProtocolEnabled', False)
904
+ return enabled
905
+ return False
906
+
907
+ def set_ntp_enabled(self, enable):
908
+ bmcinfo = self._do_web_request(self._bmcurl)
909
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
910
+ if netprotocols:
911
+ request = {'NTP':{'ProtocolEnabled': enable}}
912
+ self._do_web_request(netprotocols, request, method='PATCH')
913
+ self._do_web_request(netprotocols, cache=0)
914
+
915
+ def get_ntp_servers(self):
916
+ bmcinfo = self._do_web_request(self._bmcurl)
917
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
918
+ if not netprotocols:
919
+ return []
920
+ netprotoinfo = self._do_web_request(netprotocols)
921
+ return netprotoinfo.get('NTP', {}).get('NTPServers', [])
922
+
923
+ def set_ntp_server(self, server, index=None):
924
+ bmcinfo = self._do_web_request(self._bmcurl)
925
+ netprotocols = bmcinfo.get('NetworkProtocol', {}).get('@odata.id', None)
926
+ currntpservers = self.get_ntp_servers()
927
+ if index is None:
928
+ if server in currntpservers:
929
+ return
930
+ currntpservers = [server] + currntpservers
931
+ else:
932
+ if (index + 1) > len(currntpservers):
933
+ if not server:
934
+ return
935
+ currntpservers.append(server)
936
+ else:
937
+ if not server:
938
+ del currntpservers[index]
939
+ else:
940
+ currntpservers[index] = server
941
+ request = {'NTP':{'NTPServers': currntpservers}}
942
+ self._do_web_request(netprotocols, request, method='PATCH')
943
+ self._do_web_request(netprotocols, cache=0)
944
+
945
+
808
946
  def clear_bmc_configuration(self):
809
947
  """Reset BMC to factory default
810
948
 
@@ -816,7 +954,7 @@ class Command(object):
816
954
  rc = bmcinfo.get('Actions', {}).get('#Manager.ResetToDefaults', {})
817
955
  actinf = rc.get('ResetType@Redfish.AllowableValues', [])
818
956
  if 'ResetAll' in actinf:
819
- acturl = actinf.get('target', None)
957
+ acturl = rc.get('target', None)
820
958
  if acturl:
821
959
  self._do_web_request(acturl, {'ResetType': 'ResetAll'})
822
960
  return
@@ -962,6 +1100,7 @@ class Command(object):
962
1100
  {'HostName': hostname}, 'PATCH')
963
1101
 
964
1102
  def get_firmware(self, components=()):
1103
+ self._fwnamemap = {}
965
1104
  try:
966
1105
  for firminfo in self.oem.get_firmware_inventory(components, self):
967
1106
  yield firminfo
@@ -969,7 +1108,6 @@ class Command(object):
969
1108
  return
970
1109
  fwlist = self._do_web_request(self._fwinventory)
971
1110
  fwurls = [x['@odata.id'] for x in fwlist.get('Members', [])]
972
- self._fwnamemap = {}
973
1111
  for res in self._do_bulk_requests(fwurls):
974
1112
  res = self._extract_fwinfo(res)
975
1113
  if res[0] is None:
@@ -1072,6 +1210,10 @@ class Command(object):
1072
1210
  @property
1073
1211
  def oem(self):
1074
1212
  if not self._oem:
1213
+ if self.sysurl:
1214
+ self._do_web_request(self.sysurl, cache=False) # This is to trigger token validation and renewel
1215
+ elif self._varbmcurl:
1216
+ self._do_web_request(self._varbmcurl, cache=False) # This is to trigger token validation and renewel
1075
1217
  self._oem = oem.get_oem_handler(
1076
1218
  self.sysinfo, self.sysurl, self.wc, self._urlcache, self)
1077
1219
  self._oem.set_credentials(self.username, self.password)
@@ -1081,70 +1223,7 @@ class Command(object):
1081
1223
  return self.oem.get_description(self)
1082
1224
 
1083
1225
  def get_event_log(self, clear=False):
1084
- bmcinfo = self._do_web_request(self._bmcurl)
1085
- lsurl = bmcinfo.get('LogServices', {}).get('@odata.id', None)
1086
- if not lsurl:
1087
- return
1088
- currtime = bmcinfo.get('DateTime', None)
1089
- correction = timedelta(0)
1090
- utz = tz.tzoffset('', 0)
1091
- ltz = tz.gettz()
1092
- if currtime:
1093
- currtime = parse_time(currtime)
1094
- if currtime:
1095
- now = datetime.now(utz)
1096
- try:
1097
- correction = now - currtime
1098
- except TypeError:
1099
- correction = now - currtime.replace(tzinfo=utz)
1100
- lurls = self._do_web_request(lsurl).get('Members', [])
1101
- for lurl in lurls:
1102
- lurl = lurl['@odata.id']
1103
- loginfo = self._do_web_request(lurl, cache=(not clear))
1104
- entriesurl = loginfo.get('Entries', {}).get('@odata.id', None)
1105
- if not entriesurl:
1106
- continue
1107
- logid = loginfo.get('Id', '')
1108
- entries = self._do_web_request(entriesurl, cache=False)
1109
- if clear:
1110
- # The clear is against the log service etag, not entries
1111
- # so we have to fetch service etag after we fetch entries
1112
- # until we can verify that the etag is consistent to prove
1113
- # that the clear is atomic
1114
- newloginfo = self._do_web_request(lurl, cache=False)
1115
- clearurl = newloginfo.get('Actions', {}).get(
1116
- '#LogService.ClearLog', {}).get('target', '')
1117
- while clearurl:
1118
- try:
1119
- self._do_web_request(clearurl, method='POST',
1120
- payload={})
1121
- clearurl = False
1122
- except exc.PyghmiException as e:
1123
- if 'EtagPreconditionalFailed' not in str(e):
1124
- raise
1125
- # This doesn't guarantee atomicity, but it mitigates
1126
- # greatly. Unfortunately some implementations
1127
- # mutate the tag endlessly and we have no hope
1128
- entries = self._do_web_request(entriesurl, cache=False)
1129
- newloginfo = self._do_web_request(lurl, cache=False)
1130
- for log in entries.get('Members', []):
1131
- if ('Created' not in log and 'Message' not in log
1132
- and 'Severity' not in log):
1133
- # without any data, this log entry isn't actionable
1134
- continue
1135
- record = {}
1136
- record['log_id'] = logid
1137
- parsedtime = parse_time(log.get('Created', ''))
1138
- if parsedtime:
1139
- entime = parsedtime + correction
1140
- entime = entime.astimezone(ltz)
1141
- record['timestamp'] = entime.strftime('%Y-%m-%dT%H:%M:%S')
1142
- else:
1143
- record['timestamp'] = log.get('Created', '')
1144
- record['message'] = log.get('Message', None)
1145
- record['severity'] = _healthmap.get(
1146
- log.get('Severity', 'Warning'), const.Health.Ok)
1147
- yield record
1226
+ return self.oem.get_event_log(clear, self)
1148
1227
 
1149
1228
  def _get_chassis_env(self, chassis):
1150
1229
  chassisurl = chassis['@odata.id']
@@ -1160,35 +1239,11 @@ class Command(object):
1160
1239
  return retval
1161
1240
 
1162
1241
  def get_average_processor_temperature(self):
1163
- cputemps = []
1164
- for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1165
- thermals = self._get_thermals(chassis)
1166
- for temp in thermals:
1167
- if temp.get('PhysicalContext', '') != 'CPU':
1168
- continue
1169
- if temp.get('ReadingCelsius', None) is None:
1170
- continue
1171
- cputemps.append(temp['ReadingCelsius'])
1172
- if not cputemps:
1173
- return SensorReading(
1174
- None, {'name': 'Average Processor Temperature'}, value=None, units='°C',
1175
- unavailable=True)
1176
- avgtemp = sum(cputemps) / len(cputemps)
1177
- return SensorReading(
1178
- None, {'name': 'Average Processor Temperature'}, value=avgtemp, units='°C')
1242
+ return self.oem.get_average_processor_temperature(self)
1243
+
1179
1244
 
1180
1245
  def get_system_power_watts(self):
1181
- totalwatts = 0
1182
- gotpower = False
1183
- for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1184
- envinfo = self._get_chassis_env(chassis)
1185
- currwatts = envinfo.get('watts', None)
1186
- if currwatts is not None:
1187
- gotpower = True
1188
- totalwatts += envinfo['watts']
1189
- if not gotpower:
1190
- raise exc.UnsupportedFunctionality("System does not provide Power under redfish EnvironmentMetrics")
1191
- return totalwatts
1246
+ return self.oem.get_system_power_watts(self)
1192
1247
 
1193
1248
  def get_inlet_temperature(self):
1194
1249
  inlets = []
@@ -1222,6 +1277,16 @@ class Command(object):
1222
1277
  yield self.get_sensor_reading(sensor)
1223
1278
 
1224
1279
  def _extract_reading(self, sensor, reading):
1280
+ if sensor.get('generic', False): # generic sensor
1281
+ val = reading.get('Reading', None)
1282
+ unavail = val is None
1283
+ units = reading.get('ReadingUnits', None)
1284
+ if units == 'Cel':
1285
+ units = '°C'
1286
+ if units == 'cft_i/min':
1287
+ units = 'CFM'
1288
+ return SensorReading(reading, None, value=val, units=units,
1289
+ unavailable=unavail)
1225
1290
  if sensor['type'] == 'Fan':
1226
1291
  for fan in reading['Fans']:
1227
1292
  if fan['Name'] == sensor['name']:
@@ -1317,9 +1382,18 @@ class Command(object):
1317
1382
  if vmcoll:
1318
1383
  vmlist = self._do_web_request(vmcoll)
1319
1384
  vmurls = [x['@odata.id'] for x in vmlist.get('Members', [])]
1385
+ suspendedxauth = False
1386
+ if 'X-Auth-Token' in self.wc.stdheaders:
1387
+ suspendedxauth = True
1388
+ del self.wc.stdheaders['X-Auth-Token']
1389
+ self.wc.set_basic_credentials(self.username, self.password)
1320
1390
  try:
1321
1391
  self.oem.attach_remote_media(url, username, password, vmurls)
1322
1392
  except exc.BypassGenericBehavior:
1393
+ if suspendedxauth:
1394
+ self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken
1395
+ if 'Authorization' in self.wc.stdheaders:
1396
+ del self.wc.stdheaders['Authorization']
1323
1397
  return
1324
1398
  for vmurl in vmurls:
1325
1399
  vminfo = self._do_web_request(vmurl, cache=False)
@@ -1341,6 +1415,10 @@ class Command(object):
1341
1415
  else:
1342
1416
  raise
1343
1417
  break
1418
+ if suspendedxauth:
1419
+ self.wc.stdheaders['X-Auth-Token'] = self.xauthtoken
1420
+ if 'Authorization' in self.wc.stdheaders:
1421
+ del self.wc.stdheaders['Authorization']
1344
1422
 
1345
1423
  def detach_remote_media(self):
1346
1424
  try:
@@ -1430,3 +1508,4 @@ if __name__ == '__main__':
1430
1508
  print(repr(
1431
1509
  Command(sys.argv[1], os.environ['BMCUSER'], os.environ['BMCPASS'],
1432
1510
  verifycallback=lambda x: True).get_power()))
1511
+