pyghmi 1.5.72__py3-none-any.whl → 1.5.76__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.
@@ -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
+