pyghmi 1.5.64__tar.gz → 1.5.66__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. {pyghmi-1.5.64 → pyghmi-1.5.66}/AUTHORS +1 -0
  2. {pyghmi-1.5.64 → pyghmi-1.5.66}/ChangeLog +16 -0
  3. {pyghmi-1.5.64 → pyghmi-1.5.66}/PKG-INFO +1 -1
  4. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/command.py +12 -0
  5. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/generic.py +77 -1
  6. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/energy.py +1 -0
  7. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/imm.py +22 -8
  8. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/command.py +70 -0
  9. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/generic.py +9 -3
  10. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/lenovo/xcc.py +39 -13
  11. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/PKG-INFO +1 -1
  12. pyghmi-1.5.66/pyghmi.egg-info/pbr.json +1 -0
  13. pyghmi-1.5.64/pyghmi.egg-info/pbr.json +0 -1
  14. {pyghmi-1.5.64 → pyghmi-1.5.66}/.coveragerc +0 -0
  15. {pyghmi-1.5.64 → pyghmi-1.5.66}/.stestr.conf +0 -0
  16. {pyghmi-1.5.64 → pyghmi-1.5.66}/CONTRIBUTING.rst +0 -0
  17. {pyghmi-1.5.64 → pyghmi-1.5.66}/LICENSE +0 -0
  18. {pyghmi-1.5.64 → pyghmi-1.5.66}/MANIFEST.in +0 -0
  19. {pyghmi-1.5.64 → pyghmi-1.5.66}/README +0 -0
  20. {pyghmi-1.5.64 → pyghmi-1.5.66}/README.md +0 -0
  21. {pyghmi-1.5.64 → pyghmi-1.5.66}/builddeb +0 -0
  22. {pyghmi-1.5.64 → pyghmi-1.5.66}/buildrpm +0 -0
  23. {pyghmi-1.5.64 → pyghmi-1.5.66}/doc/requirements.txt +0 -0
  24. {pyghmi-1.5.64 → pyghmi-1.5.66}/doc/source/conf.py +0 -0
  25. {pyghmi-1.5.64 → pyghmi-1.5.66}/doc/source/contributor/index.rst +0 -0
  26. {pyghmi-1.5.64 → pyghmi-1.5.66}/doc/source/index.rst +0 -0
  27. {pyghmi-1.5.64 → pyghmi-1.5.66}/doc/source/install/index.rst +0 -0
  28. {pyghmi-1.5.64 → pyghmi-1.5.66}/doc/source/reference/index.rst +0 -0
  29. {pyghmi-1.5.64 → pyghmi-1.5.66}/makesetup +0 -0
  30. {pyghmi-1.5.64 → pyghmi-1.5.66}/playbooks/legacy/tempest-devstack-ironic-pxe_ipmitool-pyghmi-src/post.yaml +0 -0
  31. {pyghmi-1.5.64 → pyghmi-1.5.66}/playbooks/legacy/tempest-devstack-ironic-pxe_ipmitool-pyghmi-src/run.yaml +0 -0
  32. {pyghmi-1.5.64 → pyghmi-1.5.66}/py27-constraints.txt +0 -0
  33. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/__init__.py +0 -0
  34. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/cmd/__init__.py +0 -0
  35. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/cmd/fakebmc.py +0 -0
  36. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/cmd/pyghmicons.py +0 -0
  37. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/cmd/pyghmiutil.py +0 -0
  38. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/cmd/virshbmc.py +0 -0
  39. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/constants.py +0 -0
  40. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/exceptions.py +0 -0
  41. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/__init__.py +0 -0
  42. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/bmc.py +0 -0
  43. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/console.py +0 -0
  44. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/events.py +0 -0
  45. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/fru.py +0 -0
  46. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/__init__.py +0 -0
  47. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/__init__.py +0 -0
  48. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/config.py +0 -0
  49. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/cpu.py +0 -0
  50. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/dimm.py +0 -0
  51. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/drive.py +0 -0
  52. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/firmware.py +0 -0
  53. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/handler.py +0 -0
  54. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/inventory.py +0 -0
  55. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/nextscale.py +0 -0
  56. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/pci.py +0 -0
  57. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/psu.py +0 -0
  58. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/raid_controller.py +0 -0
  59. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lenovo/raid_drive.py +0 -0
  60. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/oem/lookup.py +0 -0
  61. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/__init__.py +0 -0
  62. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/constants.py +0 -0
  63. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/localsession.py +0 -0
  64. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/serversession.py +0 -0
  65. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/session.py +0 -0
  66. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/simplesession.py +0 -0
  67. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/spd.py +0 -0
  68. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/private/util.py +0 -0
  69. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/ipmi/sdr.py +0 -0
  70. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/media.py +0 -0
  71. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/__init__.py +0 -0
  72. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/__init__.py +0 -0
  73. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/dell/__init__.py +0 -0
  74. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/dell/idrac.py +0 -0
  75. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/dell/main.py +0 -0
  76. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/lenovo/__init__.py +0 -0
  77. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/lenovo/main.py +0 -0
  78. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/lenovo/tsma.py +0 -0
  79. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/redfish/oem/lookup.py +0 -0
  80. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/storage.py +0 -0
  81. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/tests/__init__.py +0 -0
  82. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/tests/unit/__init__.py +0 -0
  83. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/tests/unit/base.py +0 -0
  84. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/tests/unit/ipmi/__init__.py +0 -0
  85. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/tests/unit/ipmi/test_sdr.py +0 -0
  86. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/util/__init__.py +0 -0
  87. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/util/parse.py +0 -0
  88. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/util/webclient.py +0 -0
  89. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi/version.py +0 -0
  90. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/SOURCES.txt +0 -0
  91. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/dependency_links.txt +0 -0
  92. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/entry_points.txt +0 -0
  93. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/not-zip-safe +0 -0
  94. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/requires.txt +0 -0
  95. {pyghmi-1.5.64 → pyghmi-1.5.66}/pyghmi.egg-info/top_level.txt +0 -0
  96. {pyghmi-1.5.64 → pyghmi-1.5.66}/python-pyghmi.spec +0 -0
  97. {pyghmi-1.5.64 → pyghmi-1.5.66}/python-pyghmi.spec.tmpl +0 -0
  98. {pyghmi-1.5.64 → pyghmi-1.5.66}/requirements.txt +0 -0
  99. {pyghmi-1.5.64 → pyghmi-1.5.66}/setup.cfg +0 -0
  100. {pyghmi-1.5.64 → pyghmi-1.5.66}/setup.py +0 -0
  101. {pyghmi-1.5.64 → pyghmi-1.5.66}/setup.py.tmpl +0 -0
  102. {pyghmi-1.5.64 → pyghmi-1.5.66}/test-requirements.txt +0 -0
  103. {pyghmi-1.5.64 → pyghmi-1.5.66}/tox.ini +0 -0
  104. {pyghmi-1.5.64 → pyghmi-1.5.66}/wheezy.patch +0 -0
  105. {pyghmi-1.5.64 → pyghmi-1.5.66}/zuul.d/project.yaml +0 -0
@@ -41,6 +41,7 @@ Steve Baker <sbaker@redhat.com>
41
41
  Tim Rozet <trozet@redhat.com>
42
42
  Tovin Seven <vinhnt@vn.fujitsu.com>
43
43
  Vlad Spoiala <vspoiala1@lenovo.com>
44
+ Vlad Spoiala1 <vspoiala1@lenovo.com>
44
45
  ghanshyam <gmann@ghanshyammann.com>
45
46
  huang.zhiping <huang.zhiping@99cloud.net>
46
47
  lijingxin <lijingxin@sinorail.com>
@@ -1,6 +1,22 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ 1.5.66
5
+ ------
6
+
7
+ * Add retry mechanism to FW update
8
+
9
+ 1.5.65
10
+ ------
11
+
12
+ * Add prefix to 7mm disks
13
+ * Do not presume messages in result
14
+ * Fix total and free capacity calculations
15
+ * Have average CPU handle totally missing sensors
16
+ * Handle missing sensors
17
+ * Add normalized temperature/power data for redfish
18
+ * Add normalized sensor data
19
+
4
20
  1.5.64
5
21
  ------
6
22
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyghmi
3
- Version: 1.5.64
3
+ Version: 1.5.66
4
4
  Summary: Python General Hardware Management Initiative (IPMI and others)
5
5
  Home-page: http://github.com/openstack/pyghmi/
6
6
  Author: Jarrod Johnson
@@ -778,6 +778,18 @@ class Command(object):
778
778
  summary['badreadings'] = fallbackreadings
779
779
  return summary
780
780
 
781
+ def get_system_power_watts(self):
782
+ self.oem_init()
783
+ return self._oem.get_system_power_watts(self)
784
+
785
+ def get_inlet_temperature(self):
786
+ self.oem_init()
787
+ return self._oem.get_inlet_temperature(self)
788
+
789
+ def get_average_processor_temperature(self):
790
+ self.oem_init()
791
+ return self._oem.get_average_processor_temperature(self)
792
+
781
793
  def get_sensor_reading(self, sensorname):
782
794
  """Get a sensor reading by name
783
795
 
@@ -14,7 +14,8 @@
14
14
 
15
15
  import pyghmi.exceptions as exc
16
16
  import pyghmi.ipmi.private.constants as event_const
17
-
17
+ import pyghmi.ipmi.sdr as ipmisdr
18
+ import struct
18
19
 
19
20
  class OEMHandler(object):
20
21
  """Handler class for OEM capabilities.
@@ -41,6 +42,81 @@ class OEMHandler(object):
41
42
  """
42
43
  return {}
43
44
 
45
+ def get_system_power_watts(self, ipmicmd):
46
+ # Use DCMI getpower reading command
47
+ rsp = ipmicmd.xraw_command(netfn=0x2c, command=2, data=(0xdc, 1, 0, 0))
48
+ wattage = struct.unpack('<H', rsp['data'][1:3])[0]
49
+ return wattage
50
+
51
+ def get_average_processor_temperature(self, ipmicmd):
52
+ # DCMI suggests preferrence for 0x37 ('Air inlet')
53
+ # If not that, then 0x40 ('Air inlet')
54
+ # in practice, some implementations use 0x27 ('External environment')
55
+ if not hasattr(self, '_processor_names'):
56
+ self._processor_names = []
57
+ readings = []
58
+ if not self._processor_names:
59
+ sdr = ipmicmd.init_sdr()
60
+ for sensename in sdr.sensors:
61
+ sensor = sdr.sensors[sensename]
62
+ if sensor.reading_type != 1:
63
+ continue
64
+ if not sensor.baseunit:
65
+ continue
66
+ if sensor.sensor_type != 'Temperature':
67
+ continue
68
+ if sensor.entity == 'Processor':
69
+ self._processor_names.append(sensor.sensor_name)
70
+ readingvalues = []
71
+ for procsensor in self._processor_names:
72
+ try:
73
+ reading = ipmicmd.get_sensor_reading(procsensor)
74
+ except exc.IpmiException:
75
+ continue
76
+ if reading.value is not None:
77
+ readingvalues.append(float(reading.value))
78
+ tmplreading = ipmisdr.SensorReading({'name': 'Average Processor Temperature', 'type': 'Temperature'}, '°C')
79
+ if readingvalues:
80
+ tmplreading.value = sum(readingvalues) / len(readingvalues)
81
+ else:
82
+ tmplreading.value = None
83
+ tmplreading.unavailable = 1
84
+ return tmplreading
85
+
86
+
87
+ def get_inlet_temperature(self, ipmicmd):
88
+ # DCMI suggests preferrence for 0x37 ('Air inlet')
89
+ # If not that, then 0x40 ('Air inlet')
90
+ # in practice, some implementations use 0x27 ('External environment')
91
+ if not hasattr(self, '_inlet_name'):
92
+ self._inlet_name = None
93
+ if self._inlet_name:
94
+ return ipmicmd.get_sensor_reading(self._inlet_name)
95
+ sdr = ipmicmd.init_sdr()
96
+ extenv = []
97
+ airinlets = []
98
+ for sensename in sdr.sensors:
99
+ sensor = sdr.sensors[sensename]
100
+ if sensor.reading_type != 1:
101
+ continue
102
+ if not sensor.baseunit:
103
+ continue
104
+ if sensor.sensor_type != 'Temperature':
105
+ continue
106
+ if sensor.entity == 'External environment':
107
+ extenv.append(sensor.sensor_name)
108
+ if sensor.entity == 'Air inlet':
109
+ airinlets.append(sensor.sensor_name)
110
+ if airinlets:
111
+ if len(airinlets) > 1:
112
+ raise Exception('TODO: how to deal with multiple inlets')
113
+ self._inlet_name = airinlets[0]
114
+ elif extenv:
115
+ if len(extenv) > 1:
116
+ raise Exception('TODO: how to deal with multiple external environments')
117
+ self._inlet_name = extenv[0]
118
+ return ipmicmd.get_sensor_reading(self._inlet_name)
119
+
44
120
  def process_event(self, event, ipmicmd, seldata):
45
121
  """Modify an event according with OEM understanding.
46
122
 
@@ -28,6 +28,7 @@ class EnergyManager(object):
28
28
  # get the handle (which has always been the same, but just in case
29
29
  self.iana = bytearray(b'\x66\x4a\x00')
30
30
  self._usefapm = False
31
+ self._mypowermeters = ()
31
32
  try:
32
33
  rsp = ipmicmd.xraw_command(netfn=0x3a, command=0x32, data=[4, 2, 0, 0, 0])
33
34
  if len(rsp['data']) >= 8:
@@ -104,7 +104,7 @@ def fixup_str(propstr):
104
104
  def str_to_size(sizestr):
105
105
  if 'GB' in sizestr:
106
106
  sizestr = sizestr.replace('GB', '')
107
- sizestr = int(float(sizestr) * 1024)
107
+ sizestr = int(float(sizestr) * 1000)
108
108
  elif 'GiB' in sizestr:
109
109
  sizestr = sizestr.replace('GiB', '')
110
110
  sizestr = int(float(sizestr) * 1024)
@@ -1336,8 +1336,12 @@ class XCCClient(IMMClient):
1336
1336
 
1337
1337
  def get_disk_hardware(self, diskent, prefix=''):
1338
1338
  bdata = {}
1339
- if not prefix and diskent.get('location', '').startswith('M.2'):
1340
- prefix = 'M.2-'
1339
+ if not prefix:
1340
+ location = diskent.get('location', '')
1341
+ if location.startswith('M.2'):
1342
+ prefix = 'M.2-'
1343
+ elif location.startswith('7MM'):
1344
+ prefix = '7MM-'
1341
1345
  diskname = 'Disk {1}{0}'.format(diskent['slotNo'], prefix)
1342
1346
  bdata['Model'] = diskent['productName'].rstrip()
1343
1347
  bdata['Serial Number'] = diskent['serialNo'].rstrip()
@@ -1347,8 +1351,12 @@ class XCCClient(IMMClient):
1347
1351
 
1348
1352
  def get_disk_firmware(self, diskent, prefix=''):
1349
1353
  bdata = {}
1350
- if not prefix and diskent.get('location', '').startswith('M.2'):
1351
- prefix = 'M.2-'
1354
+ if not prefix:
1355
+ location = diskent.get('location', '')
1356
+ if location.startswith('M.2'):
1357
+ prefix = 'M.2-'
1358
+ elif location.startswith('7MM'):
1359
+ prefix = '7MM-'
1352
1360
  diskname = 'Disk {1}{0}'.format(diskent['slotNo'], prefix)
1353
1361
  bdata['model'] = diskent[
1354
1362
  'productName'].rstrip()
@@ -2054,13 +2062,18 @@ class XCCClient(IMMClient):
2054
2062
  complete = False
2055
2063
  phase = "apply"
2056
2064
  statetype = 'TaskState'
2057
- while not complete:
2065
+ # sometimes we get an empty pgress when transitioning from the apply phase to
2066
+ # the validating phase; add a retry here so we don't exit the loop in this case
2067
+ retry = 3
2068
+ while not complete and retry > 0:
2058
2069
  pgress, status = self.grab_redfish_response_with_status(
2059
2070
  monitorurl)
2060
2071
  if status < 200 or status >= 300:
2061
2072
  raise Exception(pgress)
2062
2073
  if not pgress:
2063
- break
2074
+ retry -= 1
2075
+ ipmisession.Session.pause(3)
2076
+ continue
2064
2077
  for msg in pgress.get('Messages', []):
2065
2078
  if 'Verify failed' in msg.get('Message', ''):
2066
2079
  raise Exception(msg['Message'])
@@ -2072,7 +2085,8 @@ class XCCClient(IMMClient):
2072
2085
  complete = state == 'Completed'
2073
2086
  progress({'phase': phase, 'progress': pct})
2074
2087
  if complete:
2075
- if 'OperationTransitionedToJob' in pgress['Messages'][0]['MessageId']:
2088
+ msgs = pgress.get('Messages', [])
2089
+ if msgs and 'OperationTransitionedToJob' in msgs[0].get('MessageId', ''):
2076
2090
  monitorurl = pgress['Messages'][0]['MessageArgs'][0]
2077
2091
  phase = 'validating'
2078
2092
  statetype = 'JobState'
@@ -142,6 +142,16 @@ class SensorReading(object):
142
142
  self.imprecision = None
143
143
  self.units = units
144
144
  self.unavailable = unavailable
145
+
146
+ def __repr__(self):
147
+ return repr({
148
+ 'value': self.value,
149
+ 'state_ids': self.state_ids,
150
+ 'units': self.units,
151
+ 'imprecision': self.imprecision,
152
+ 'name': self.name,
153
+ 'unavailable': self.unavailable,
154
+ })
145
155
 
146
156
 
147
157
  class Command(object):
@@ -509,6 +519,8 @@ class Command(object):
509
519
  def _do_web_request(self, url, payload=None, method=None, cache=True,
510
520
  etag=None):
511
521
  res = None
522
+ if cache is True:
523
+ cache = 30
512
524
  if cache and payload is None and method is None:
513
525
  res = self._get_cache(url, cache)
514
526
  if res:
@@ -652,6 +664,14 @@ class Command(object):
652
664
  for subchassis in chassisinfo.get('Links', {}).get('Contains', []):
653
665
  self._mapchassissensors(subchassis)
654
666
 
667
+ def _get_thermals(self, chassis):
668
+ chassisurl = chassis['@odata.id']
669
+ chassisinfo = self._do_web_request(chassisurl)
670
+ thermurl = chassisinfo.get('Thermal', {}).get('@odata.id', '')
671
+ if thermurl:
672
+ therminf = self._do_web_request(thermurl, cache=1)
673
+ return therminf.get('Temperatures', [])
674
+
655
675
  @property
656
676
  def _bmcurl(self):
657
677
  if not self._varbmcurl:
@@ -1129,6 +1149,56 @@ class Command(object):
1129
1149
  log.get('Severity', 'Warning'), const.Health.Ok)
1130
1150
  yield record
1131
1151
 
1152
+ def _get_chassis_env(self, chassis):
1153
+ chassisurl = chassis['@odata.id']
1154
+ chassisinfo = self._do_web_request(chassisurl)
1155
+ envurl = chassisinfo.get('EnvironmentMetrics', {}).get('@odata.id', '')
1156
+ envmetric = self._do_web_request(envurl, cache=1)
1157
+ retval = {
1158
+ 'watts': envmetric.get('PowerWatts', {}).get('Reading', None),
1159
+ 'inlet': envmetric.get('TemperatureCelsius', {}).get('Reading', None)
1160
+ }
1161
+ return retval
1162
+
1163
+ def get_average_processor_temperature(self):
1164
+ cputemps = []
1165
+ for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1166
+ thermals = self._get_thermals(chassis)
1167
+ for temp in thermals:
1168
+ if temp.get('PhysicalContext', '') != 'CPU':
1169
+ continue
1170
+ if temp.get('ReadingCelsius', None) is None:
1171
+ continue
1172
+ cputemps.append(temp['ReadingCelsius'])
1173
+ if not cputemps:
1174
+ return SensorReading(
1175
+ None, {'name': 'Average Processor Temperature'}, value=None, units='°C',
1176
+ unavailable=True)
1177
+ avgtemp = sum(cputemps) / len(cputemps)
1178
+ return SensorReading(
1179
+ None, {'name': 'Average Processor Temperature'}, value=avgtemp, units='°C')
1180
+
1181
+ def get_system_power_watts(self):
1182
+ totalwatts = 0
1183
+ for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1184
+ envinfo = self._get_chassis_env(chassis)
1185
+ totalwatts += envinfo['watts']
1186
+ return totalwatts
1187
+
1188
+ def get_inlet_temperature(self):
1189
+ inlets = []
1190
+ for chassis in self.sysinfo.get('Links', {}).get('Chassis', []):
1191
+ envinfo = self._get_chassis_env(chassis)
1192
+ inlets.append(envinfo['inlet'])
1193
+ if inlets:
1194
+ val = sum(inlets) / len(inlets)
1195
+ unavail = False
1196
+ else:
1197
+ val = None
1198
+ unavail = True
1199
+ return SensorReading(
1200
+ None, {'name': 'Inlet Temperature'}, value=val, units='°C',
1201
+ unavailable=unavail)
1132
1202
  def get_sensor_descriptions(self):
1133
1203
  for sensor in natural_sort(self._sensormap):
1134
1204
  yield self._sensormap[sensor]
@@ -820,10 +820,15 @@ class OEMHandler(object):
820
820
  complete = False
821
821
  phase = "apply"
822
822
  statetype = 'TaskState'
823
- while not complete:
823
+ # sometimes we get an empty pgress when transitioning from the apply phase to
824
+ # the validating phase; add a retry here so we don't exit the loop in this case
825
+ retry = 3
826
+ while not complete and retry > 0:
824
827
  pgress = self._do_web_request(monitorurl, cache=False)
825
828
  if not pgress:
826
- break
829
+ retry -= 1
830
+ time.sleep(3)
831
+ continue
827
832
  for msg in pgress.get('Messages', []):
828
833
  if 'Verify failed' in msg.get('Message', ''):
829
834
  raise Exception(msg['Message'])
@@ -836,7 +841,8 @@ class OEMHandler(object):
836
841
  complete = state == 'Completed'
837
842
  progress({'phase': phase, 'progress': pct})
838
843
  if complete:
839
- if 'OperationTransitionedToJob' in pgress['Messages'][0]['MessageId']:
844
+ msgs = pgress.get('Messages', [])
845
+ if msgs and 'OperationTransitionedToJob' in msgs[0].get('MessageId', ''):
840
846
  monitorurl = pgress['Messages'][0]['MessageArgs'][0]
841
847
  phase = 'validating'
842
848
  statetype = 'JobState'
@@ -102,6 +102,22 @@ def natural_sort(iterable):
102
102
  return sorted(iterable)
103
103
 
104
104
 
105
+ def str_to_size(sizestr):
106
+ if 'GB' in sizestr:
107
+ sizestr = sizestr.replace('GB', '')
108
+ sizestr = int(float(sizestr) * 1000)
109
+ elif 'GiB' in sizestr:
110
+ sizestr = sizestr.replace('GiB', '')
111
+ sizestr = int(float(sizestr) * 1024)
112
+ elif 'TB' in sizestr:
113
+ sizestr = sizestr.replace('TB', '')
114
+ sizestr = int(float(sizestr) * 1000 * 1000)
115
+ elif 'TiB' in sizestr:
116
+ sizestr = sizestr.replace('TiB', '')
117
+ sizestr = int(float(sizestr) * 1024 * 1024)
118
+ return sizestr
119
+
120
+
105
121
  class OEMHandler(generic.OEMHandler):
106
122
  logouturl = '/api/providers/logout'
107
123
  bmcname = 'XCC'
@@ -630,8 +646,12 @@ class OEMHandler(generic.OEMHandler):
630
646
 
631
647
  def _get_disk_firmware_single(self, diskent, prefix=''):
632
648
  bdata = {}
633
- if not prefix and diskent.get('location', '').startswith('M.2'):
634
- prefix = 'M.2-'
649
+ if not prefix:
650
+ location = diskent.get('location', '')
651
+ if location.startswith('M.2'):
652
+ prefix = 'M.2-'
653
+ elif location.startswith('7MM'):
654
+ prefix = '7MM-'
635
655
  diskname = 'Disk {1}{0}'.format(diskent['slotNo'], prefix)
636
656
  bdata['model'] = diskent[
637
657
  'productName'].rstrip()
@@ -708,12 +728,8 @@ class OEMHandler(generic.OEMHandler):
708
728
  spares.append(diskinfo)
709
729
  else:
710
730
  disks.append(diskinfo)
711
- totalsize = pool['totalCapacityStr'].replace(
712
- 'GB', '').replace('GiB', '')
713
- totalsize = int(float(totalsize) * 1024)
714
- freesize = pool['freeCapacityStr'].replace(
715
- 'GB', '').replace('GiB', '')
716
- freesize = int(float(freesize) * 1024)
731
+ totalsize = str_to_size(pool['totalCapacityStr'])
732
+ freesize = str_to_size(pool['freeCapacityStr'])
717
733
  pools.append(storage.Array(
718
734
  disks=disks, raid=pool['rdlvlstr'], volumes=volumes,
719
735
  id=(cid, pool['id']), hotspares=spares,
@@ -1262,10 +1278,15 @@ class OEMHandler(generic.OEMHandler):
1262
1278
  complete = False
1263
1279
  phase = "apply"
1264
1280
  statetype = 'TaskState'
1265
- while not complete:
1281
+ # sometimes we get an empty pgress when transitioning from the apply phase to
1282
+ # the validating phase; add a retry here so we don't exit the loop in this case
1283
+ retry = 3
1284
+ while not complete and retry > 0:
1266
1285
  pgress = self._do_web_request(monitorurl, cache=False)
1267
1286
  if not pgress:
1268
- break
1287
+ retry -= 1
1288
+ time.sleep(3)
1289
+ continue
1269
1290
  for msg in pgress.get('Messages', []):
1270
1291
  if 'Verify failed' in msg.get('Message', ''):
1271
1292
  raise Exception(msg['Message'])
@@ -1278,7 +1299,8 @@ class OEMHandler(generic.OEMHandler):
1278
1299
  complete = state == 'Completed'
1279
1300
  progress({'phase': phase, 'progress': pct})
1280
1301
  if complete:
1281
- if 'OperationTransitionedToJob' in pgress['Messages'][0]['MessageId']:
1302
+ msgs = pgress.get('Messages', [])
1303
+ if msgs and 'OperationTransitionedToJob' in msgs[0].get('MessageId', ''):
1282
1304
  monitorurl = pgress['Messages'][0]['MessageArgs'][0]
1283
1305
  phase = 'validating'
1284
1306
  statetype = 'JobState'
@@ -1816,8 +1838,12 @@ class OEMHandler(generic.OEMHandler):
1816
1838
 
1817
1839
  def get_disk_hardware(self, diskent, prefix=''):
1818
1840
  bdata = {}
1819
- if not prefix and diskent.get('location', '').startswith('M.2'):
1820
- prefix = 'M.2-'
1841
+ if not prefix:
1842
+ location = diskent.get('location', '')
1843
+ if location.startswith('M.2'):
1844
+ prefix = 'M.2-'
1845
+ elif location.startswith('7MM'):
1846
+ prefix = '7MM-'
1821
1847
  diskname = 'Disk {1}{0}'.format(diskent['slotNo'], prefix)
1822
1848
  bdata['Model'] = diskent['productName'].rstrip()
1823
1849
  bdata['Serial Number'] = diskent['serialNo'].rstrip()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyghmi
3
- Version: 1.5.64
3
+ Version: 1.5.66
4
4
  Summary: Python General Hardware Management Initiative (IPMI and others)
5
5
  Home-page: http://github.com/openstack/pyghmi/
6
6
  Author: Jarrod Johnson
@@ -0,0 +1 @@
1
+ {"git_version": "4d6921c", "is_release": true}
@@ -1 +0,0 @@
1
- {"git_version": "b679122", "is_release": true}
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes