pyghmi 1.5.70__tar.gz → 1.5.72__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. {pyghmi-1.5.70 → pyghmi-1.5.72}/ChangeLog +25 -0
  2. {pyghmi-1.5.70 → pyghmi-1.5.72}/PKG-INFO +1 -1
  3. {pyghmi-1.5.70 → pyghmi-1.5.72}/builddeb +5 -2
  4. pyghmi-1.5.72/makesetup +14 -0
  5. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/command.py +2 -2
  6. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/config.py +3 -1
  7. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/handler.py +2 -2
  8. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/imm.py +2 -2
  9. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/command.py +19 -22
  10. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/generic.py +227 -77
  11. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/lenovo/main.py +14 -1
  12. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/lenovo/xcc.py +14 -7
  13. pyghmi-1.5.72/pyghmi/redfish/oem/lenovo/xcc3.py +137 -0
  14. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/PKG-INFO +1 -1
  15. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/SOURCES.txt +1 -0
  16. pyghmi-1.5.72/pyghmi.egg-info/pbr.json +1 -0
  17. pyghmi-1.5.70/makesetup +0 -10
  18. pyghmi-1.5.70/pyghmi.egg-info/pbr.json +0 -1
  19. {pyghmi-1.5.70 → pyghmi-1.5.72}/.coveragerc +0 -0
  20. {pyghmi-1.5.70 → pyghmi-1.5.72}/.stestr.conf +0 -0
  21. {pyghmi-1.5.70 → pyghmi-1.5.72}/AUTHORS +0 -0
  22. {pyghmi-1.5.70 → pyghmi-1.5.72}/CONTRIBUTING.rst +0 -0
  23. {pyghmi-1.5.70 → pyghmi-1.5.72}/LICENSE +0 -0
  24. {pyghmi-1.5.70 → pyghmi-1.5.72}/MANIFEST.in +0 -0
  25. {pyghmi-1.5.70 → pyghmi-1.5.72}/README +0 -0
  26. {pyghmi-1.5.70 → pyghmi-1.5.72}/README.md +0 -0
  27. {pyghmi-1.5.70 → pyghmi-1.5.72}/buildrpm +0 -0
  28. {pyghmi-1.5.70 → pyghmi-1.5.72}/doc/requirements.txt +0 -0
  29. {pyghmi-1.5.70 → pyghmi-1.5.72}/doc/source/conf.py +0 -0
  30. {pyghmi-1.5.70 → pyghmi-1.5.72}/doc/source/contributor/index.rst +0 -0
  31. {pyghmi-1.5.70 → pyghmi-1.5.72}/doc/source/index.rst +0 -0
  32. {pyghmi-1.5.70 → pyghmi-1.5.72}/doc/source/install/index.rst +0 -0
  33. {pyghmi-1.5.70 → pyghmi-1.5.72}/doc/source/reference/index.rst +0 -0
  34. {pyghmi-1.5.70 → pyghmi-1.5.72}/playbooks/legacy/tempest-devstack-ironic-pxe_ipmitool-pyghmi-src/post.yaml +0 -0
  35. {pyghmi-1.5.70 → pyghmi-1.5.72}/playbooks/legacy/tempest-devstack-ironic-pxe_ipmitool-pyghmi-src/run.yaml +0 -0
  36. {pyghmi-1.5.70 → pyghmi-1.5.72}/py27-constraints.txt +0 -0
  37. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/__init__.py +0 -0
  38. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/cmd/__init__.py +0 -0
  39. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/cmd/fakebmc.py +0 -0
  40. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/cmd/pyghmicons.py +0 -0
  41. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/cmd/pyghmiutil.py +0 -0
  42. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/cmd/virshbmc.py +0 -0
  43. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/constants.py +0 -0
  44. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/exceptions.py +0 -0
  45. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/__init__.py +0 -0
  46. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/bmc.py +0 -0
  47. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/console.py +0 -0
  48. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/events.py +0 -0
  49. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/fru.py +0 -0
  50. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/__init__.py +0 -0
  51. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/generic.py +0 -0
  52. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/__init__.py +0 -0
  53. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/cpu.py +0 -0
  54. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/dimm.py +0 -0
  55. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/drive.py +0 -0
  56. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/energy.py +0 -0
  57. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/firmware.py +0 -0
  58. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/inventory.py +0 -0
  59. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/nextscale.py +0 -0
  60. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/pci.py +0 -0
  61. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/psu.py +0 -0
  62. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/raid_controller.py +0 -0
  63. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lenovo/raid_drive.py +0 -0
  64. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/oem/lookup.py +0 -0
  65. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/__init__.py +0 -0
  66. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/constants.py +0 -0
  67. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/localsession.py +0 -0
  68. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/serversession.py +0 -0
  69. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/session.py +0 -0
  70. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/simplesession.py +0 -0
  71. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/spd.py +0 -0
  72. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/private/util.py +0 -0
  73. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/ipmi/sdr.py +0 -0
  74. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/media.py +0 -0
  75. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/__init__.py +0 -0
  76. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/__init__.py +0 -0
  77. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/dell/__init__.py +0 -0
  78. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/dell/idrac.py +0 -0
  79. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/dell/main.py +0 -0
  80. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/lenovo/__init__.py +0 -0
  81. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/lenovo/tsma.py +0 -0
  82. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/redfish/oem/lookup.py +0 -0
  83. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/storage.py +0 -0
  84. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/tests/__init__.py +0 -0
  85. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/tests/unit/__init__.py +0 -0
  86. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/tests/unit/base.py +0 -0
  87. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/tests/unit/ipmi/__init__.py +0 -0
  88. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/tests/unit/ipmi/test_sdr.py +0 -0
  89. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/util/__init__.py +0 -0
  90. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/util/parse.py +0 -0
  91. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/util/webclient.py +0 -0
  92. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi/version.py +0 -0
  93. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/dependency_links.txt +0 -0
  94. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/entry_points.txt +0 -0
  95. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/not-zip-safe +0 -0
  96. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/requires.txt +0 -0
  97. {pyghmi-1.5.70 → pyghmi-1.5.72}/pyghmi.egg-info/top_level.txt +0 -0
  98. {pyghmi-1.5.70 → pyghmi-1.5.72}/python-pyghmi.spec +0 -0
  99. {pyghmi-1.5.70 → pyghmi-1.5.72}/python-pyghmi.spec.tmpl +0 -0
  100. {pyghmi-1.5.70 → pyghmi-1.5.72}/requirements.txt +0 -0
  101. {pyghmi-1.5.70 → pyghmi-1.5.72}/setup.cfg +0 -0
  102. {pyghmi-1.5.70 → pyghmi-1.5.72}/setup.py +0 -0
  103. {pyghmi-1.5.70 → pyghmi-1.5.72}/setup.py.tmpl +0 -0
  104. {pyghmi-1.5.70 → pyghmi-1.5.72}/test-requirements.txt +0 -0
  105. {pyghmi-1.5.70 → pyghmi-1.5.72}/tox.ini +0 -0
  106. {pyghmi-1.5.70 → pyghmi-1.5.72}/wheezy.patch +0 -0
  107. {pyghmi-1.5.70 → pyghmi-1.5.72}/zuul.d/project.yaml +0 -0
@@ -1,6 +1,31 @@
1
1
  CHANGES
2
2
  =======
3
3
 
4
+ 1.5.72
5
+ ------
6
+
7
+ * Rework get description to enable it to work fer XCC3
8
+ * Modify build for snap versions
9
+ * Amend deb package build to avoid extranoeus content
10
+ * Use approved method to determine XCC vintage
11
+ * Implement fixup of firmware inventory
12
+ * Prefer XCC3 detection over XCC2
13
+ * Fix str concatenation on redfish spec violations
14
+ * Refactor redfish settings and leverage in XCC3
15
+
16
+ 1.5.71
17
+ ------
18
+
19
+ * Wire up redfish for getting extended BMC settings
20
+ * Add generic redfish license handling
21
+ * Extend MAC collection in redfish
22
+ * Normalize UUID format
23
+ * Add multipart update support to redfish updates
24
+ * Add BMC reset to redfish implementation
25
+ * Fix the extended bmc configuration
26
+ * Exposed advanced, extended BMC configuration
27
+ * Restore UEFI configuration to IMM systems
28
+
4
29
  1.5.70
5
30
  ------
6
31
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 1.1
2
2
  Name: pyghmi
3
- Version: 1.5.70
3
+ Version: 1.5.72
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
@@ -11,7 +11,10 @@ if grep wheezy /etc/os-release; then
11
11
  export DSCARGS=""
12
12
  fi
13
13
  # If not PBR, use the setup.py.tmpl
14
- $PYEXEC -c 'import pbr' || ./makesetup
14
+ sed -e 's/~/./' ./makesetup > ./makesetup.deb
15
+ chmod +x ./makesetup.deb
16
+ ./makesetup.deb
17
+ #$PYEXEC -c 'import pbr' || ./makesetup
15
18
  VERSION=`$PYEXEC setup.py --version`
16
19
  $PYEXEC setup.py sdist
17
20
  py2dsc $DSCARGS dist/pyghmi-$VERSION.tar.gz
@@ -21,4 +24,4 @@ dpkg-buildpackage -rfakeroot -uc -us -i
21
24
  cd -
22
25
  rm -rf $(find deb_dist -mindepth 1 -maxdepth 1 -type d)
23
26
  rm $1/*pyghmi*
24
- cp deb_dist/* $1/
27
+ cp deb_dist/*.deb $1/
@@ -0,0 +1,14 @@
1
+ #!/bin/sh
2
+ cd `dirname $0`
3
+ VERSION=`git describe|cut -d- -f 1`
4
+ NUMCOMMITS=`git describe|cut -d- -f 2`
5
+ if [ "$NUMCOMMITS" != "$VERSION" ]; then
6
+ LASTNUM=$(echo $VERSION|rev|cut -d . -f 1|rev)
7
+ LASTNUM=$((LASTNUM+1))
8
+ FIRSTPART=$(echo $VERSION|rev|cut -d . -f 2- |rev)
9
+ VERSION=${FIRSTPART}.${LASTNUM}
10
+ VERSION=$VERSION~dev$NUMCOMMITS+`git describe|cut -d- -f 3`
11
+ fi
12
+ echo $VERSION > VERSION
13
+ sed -e "s/#VERSION#/$VERSION/" setup.py.tmpl > setup.py
14
+
@@ -849,9 +849,9 @@ class Command(object):
849
849
  else:
850
850
  raise Exception("Unrecognized data format " + repr(fetchdata))
851
851
 
852
- def get_extended_bmc_configuration(self):
852
+ def get_extended_bmc_configuration(self, hideadvanced=True):
853
853
  self.oem_init()
854
- return self._oem.get_extended_bmc_configuration()
854
+ return self._oem.get_extended_bmc_configuration(hideadvanced=hideadvanced)
855
855
 
856
856
  def get_bmc_configuration(self):
857
857
  self.oem_init()
@@ -272,7 +272,9 @@ class LenovoFirmwareConfig(object):
272
272
  cfgfilename = "config"
273
273
  options = {}
274
274
  data = None
275
- if self.connection:
275
+ if not hasattr(self.xc, 'grab_redfish_response_with_status'):
276
+ rsp = ({}, 500)
277
+ elif self.connection:
276
278
  rsp = ({}, 200)
277
279
  else:
278
280
  rsp = self.xc.grab_redfish_response_with_status(
@@ -1194,9 +1194,9 @@ class OEMHandler(generic.OEMHandler):
1194
1194
  return {'height': self._fpc_variant & 0xf, 'slot': 0}
1195
1195
  return super(OEMHandler, self).get_description()
1196
1196
 
1197
- def get_extended_bmc_configuration(self):
1197
+ def get_extended_bmc_configuration(self, hideadvanced=True):
1198
1198
  if self.has_xcc:
1199
- return self.immhandler.get_extended_bmc_configuration()
1199
+ return self.immhandler.get_extended_bmc_configuration(hideadvanced=hideadvanced)
1200
1200
  return super(OEMHandler, self).get_extended_bmc_configuration()
1201
1201
 
1202
1202
  def get_bmc_configuration(self):
@@ -978,8 +978,8 @@ class XCCClient(IMMClient):
978
978
  return {}
979
979
  return {'height': int(dsc['u-height']), 'slot': int(dsc['slot'])}
980
980
 
981
- def get_extended_bmc_configuration(self):
982
- immsettings = self.get_system_configuration(fetchimm=True)
981
+ def get_extended_bmc_configuration(self, hideadvanced=True):
982
+ immsettings = self.get_system_configuration(fetchimm=True, hideadvanced=hideadvanced)
983
983
  for setting in list(immsettings):
984
984
  if not setting.startswith('IMM.'):
985
985
  del immsettings[setting]
@@ -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(
@@ -547,7 +533,7 @@ class Command(object):
547
533
  msgid = ','.join(msgid)
548
534
  raise exc.RedfishError(errmsg, msgid=msgid)
549
535
  except (ValueError, KeyError):
550
- raise exc.PyghmiException(str(url) + ":" + res[0])
536
+ raise exc.PyghmiException(str(url) + ":" + str(res[0]))
551
537
  if payload is None and method is None:
552
538
  self._urlcache[url] = {'contents': res[0],
553
539
  'vintage': os.times()[4]}
@@ -789,6 +775,9 @@ class Command(object):
789
775
  def get_health(self, verbose=True):
790
776
  return self.oem.get_health(self, verbose)
791
777
 
778
+ def get_extended_bmc_configuration(self, hideadvanced=True):
779
+ return self.oem.get_extended_bmc_configuration(self, hideadvanced=hideadvanced)
780
+
792
781
  def get_bmc_configuration(self):
793
782
  """Get miscellaneous BMC configuration
794
783
 
@@ -823,8 +812,16 @@ class Command(object):
823
812
  In many cases, this may render remote network access impracticle or
824
813
  impossible."
825
814
  """
815
+ bmcinfo = self._do_web_request(self._bmcurl)
816
+ rc = bmcinfo.get('Actions', {}).get('#Manager.ResetToDefaults', {})
817
+ actinf = rc.get('ResetType@Redfish.AllowableValues', [])
818
+ if 'ResetAll' in actinf:
819
+ acturl = actinf.get('target', None)
820
+ if acturl:
821
+ self._do_web_request(acturl, {'ResetType': 'ResetAll'})
822
+ return
826
823
  raise exc.UnsupportedFunctionality(
827
- 'Clear BMC configuration not supported in redfish yet')
824
+ 'Clear BMC configuration not supported on this platform')
828
825
 
829
826
  def get_system_configuration(self, hideadvanced=True):
830
827
  return self.oem.get_system_configuration(hideadvanced, self)
@@ -966,7 +963,7 @@ class Command(object):
966
963
 
967
964
  def get_firmware(self, components=()):
968
965
  try:
969
- for firminfo in self.oem.get_firmware_inventory(components):
966
+ for firminfo in self.oem.get_firmware_inventory(components, self):
970
967
  yield firminfo
971
968
  except exc.BypassGenericBehavior:
972
969
  return
@@ -1081,7 +1078,7 @@ class Command(object):
1081
1078
  return self._oem
1082
1079
 
1083
1080
  def get_description(self):
1084
- return self.oem.get_description()
1081
+ return self.oem.get_description(self)
1085
1082
 
1086
1083
  def get_event_log(self, clear=False):
1087
1084
  bmcinfo = self._do_web_request(self._bmcurl)
@@ -1413,20 +1410,20 @@ class Command(object):
1413
1410
  return self.oem.get_diagnostic_data(savefile, progress, autosuffix)
1414
1411
 
1415
1412
  def get_licenses(self):
1416
- return self.oem.get_licenses()
1413
+ return self.oem.get_licenses(self)
1417
1414
 
1418
1415
  def delete_license(self, name):
1419
- return self.oem.delete_license(name)
1416
+ return self.oem.delete_license(name, self)
1420
1417
 
1421
1418
  def save_licenses(self, directory):
1422
1419
  if os.path.exists(directory) and not os.path.isdir(directory):
1423
1420
  raise exc.InvalidParameterValue(
1424
1421
  'Not allowed to overwrite existing file: {0}'.format(
1425
1422
  directory))
1426
- return self.oem.save_licenses(directory)
1423
+ return self.oem.save_licenses(directory, self)
1427
1424
 
1428
1425
  def apply_license(self, filename, progress=None, data=None):
1429
- return self.oem.apply_license(filename, progress, data)
1426
+ return self.oem.apply_license(filename, self, progress, data)
1430
1427
 
1431
1428
 
1432
1429
  if __name__ == '__main__':
@@ -12,6 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
+ import base64
15
16
  from fnmatch import fnmatch
16
17
  import json
17
18
  import os
@@ -43,6 +44,29 @@ class SensorReading(object):
43
44
  self.units = units
44
45
  self.unavailable = unavailable
45
46
 
47
+
48
+ def _to_boolean(attrval):
49
+ attrval = attrval.lower()
50
+ if not attrval:
51
+ return False
52
+ if ('true'.startswith(attrval) or 'yes'.startswith(attrval)
53
+ or 'enabled'.startswith(attrval) or attrval == '1'):
54
+ return True
55
+ if ('false'.startswith(attrval) or 'no'.startswith(attrval)
56
+ or 'disabled'.startswith(attrval) or attrval == '0'):
57
+ return False
58
+ raise Exception(
59
+ 'Unrecognized candidate for boolean: {0}'.format(attrval))
60
+
61
+
62
+ def _normalize_mac(mac):
63
+ if ':' not in mac:
64
+ mac = ':'.join((
65
+ mac[:2], mac[2:4], mac[4:6],
66
+ mac[6:8], mac[8:10], mac[10:12]))
67
+ return mac.lower()
68
+
69
+
46
70
  _healthmap = {
47
71
  'Critical': const.Health.Critical,
48
72
  'Unknown': const.Health.Warning,
@@ -339,6 +363,38 @@ class OEMHandler(object):
339
363
  def get_system_configuration(self, hideadvanced=True, fishclient=None):
340
364
  return self._getsyscfg(fishclient)[0]
341
365
 
366
+ def _get_attrib_registry(self, fishclient, attribreg):
367
+ overview = fishclient._do_web_request('/redfish/v1/')
368
+ reglist = overview['Registries']['@odata.id']
369
+ reglist = fishclient._do_web_request(reglist)
370
+ regurl = None
371
+ for cand in reglist.get('Members', []):
372
+ cand = cand.get('@odata.id', '')
373
+ candname = cand.split('/')[-1]
374
+ if candname == '': # implementation uses trailing slash
375
+ candname = cand.split('/')[-2]
376
+ if candname == attribreg:
377
+ regurl = cand
378
+ break
379
+ if not regurl:
380
+ # Workaround a vendor bug where they link to a
381
+ # non-existant name
382
+ for cand in reglist.get('Members', []):
383
+ cand = cand.get('@odata.id', '')
384
+ candname = cand.split('/')[-1]
385
+ candname = candname.split('.')[0]
386
+ if candname == attribreg.split('.')[0]:
387
+ regurl = cand
388
+ break
389
+ if regurl:
390
+ reginfo = fishclient._do_web_request(regurl)
391
+ for reg in reginfo.get('Location', []):
392
+ if reg.get('Language', 'en').startswith('en'):
393
+ reguri = reg['Uri']
394
+ reginfo = self._get_biosreg(reguri, fishclient)
395
+ return reginfo
396
+ extrainfo, valtodisplay, _, self.attrdeps = reginfo
397
+
342
398
  def _getsyscfg(self, fishclient):
343
399
  biosinfo = self._do_web_request(fishclient._biosurl, cache=False)
344
400
  reginfo = ({}, {}, {}, {})
@@ -346,36 +402,9 @@ class OEMHandler(object):
346
402
  valtodisplay = {}
347
403
  self.attrdeps = {'Dependencies': [], 'Attributes': []}
348
404
  if 'AttributeRegistry' in biosinfo:
349
- overview = fishclient._do_web_request('/redfish/v1/')
350
- reglist = overview['Registries']['@odata.id']
351
- reglist = fishclient._do_web_request(reglist)
352
- regurl = None
353
- for cand in reglist.get('Members', []):
354
- cand = cand.get('@odata.id', '')
355
- candname = cand.split('/')[-1]
356
- if candname == '': # implementation uses trailing slash
357
- candname = cand.split('/')[-2]
358
- if candname == biosinfo['AttributeRegistry']:
359
- regurl = cand
360
- break
361
- if not regurl:
362
- # Workaround a vendor bug where they link to a
363
- # non-existant name
364
- for cand in reglist.get('Members', []):
365
- cand = cand.get('@odata.id', '')
366
- candname = cand.split('/')[-1]
367
- candname = candname.split('.')[0]
368
- if candname == biosinfo[
369
- 'AttributeRegistry'].split('.')[0]:
370
- regurl = cand
371
- break
372
- if regurl:
373
- reginfo = fishclient._do_web_request(regurl)
374
- for reg in reginfo.get('Location', []):
375
- if reg.get('Language', 'en').startswith('en'):
376
- reguri = reg['Uri']
377
- reginfo = self._get_biosreg(reguri, fishclient)
378
- extrainfo, valtodisplay, _, self.attrdeps = reginfo
405
+ reginfo = self._get_attrib_registry(fishclient, biosinfo['AttributeRegistry'])
406
+ if reginfo:
407
+ extrainfo, valtodisplay, _, self.attrdeps = reginfo
379
408
  currsettings = {}
380
409
  try:
381
410
  pendingsettings = fishclient._do_web_request(
@@ -412,10 +441,19 @@ class OEMHandler(object):
412
441
  rawsettings = fishclient._do_web_request(fishclient._biosurl,
413
442
  cache=False)
414
443
  rawsettings = rawsettings.get('Attributes', {})
415
- pendingsettings = fishclient._do_web_request(fishclient._setbiosurl)
444
+ pendingsettings = fishclient._do_web_request(
445
+ fishclient._setbiosurl)
446
+ return self._set_redfish_settings(
447
+ changeset, fishclient, currsettings, rawsettings,
448
+ pendingsettings, self.attrdeps, reginfo,
449
+ fishclient._setbiosurl)
450
+
451
+ def _set_redfish_settings(self, changeset, fishclient, currsettings,
452
+ rawsettings, pendingsettings, attrdeps, reginfo,
453
+ seturl):
416
454
  etag = pendingsettings.get('@odata.etag', None)
417
455
  pendingsettings = pendingsettings.get('Attributes', {})
418
- dephandler = AttrDependencyHandler(self.attrdeps, rawsettings,
456
+ dephandler = AttrDependencyHandler(attrdeps, rawsettings,
419
457
  pendingsettings)
420
458
  for change in list(changeset):
421
459
  if change not in currsettings:
@@ -434,7 +472,7 @@ class OEMHandler(object):
434
472
  changeval = changeset[change]
435
473
  overrides, blameattrs = dephandler.get_overrides(change)
436
474
  meta = {}
437
- for attr in self.attrdeps['Attributes']:
475
+ for attr in attrdeps['Attributes']:
438
476
  if attr['AttributeName'] == change:
439
477
  meta = dict(attr)
440
478
  break
@@ -473,7 +511,7 @@ class OEMHandler(object):
473
511
  changeset[change] = _to_boolean(changeset[change])
474
512
  redfishsettings = {'Attributes': changeset}
475
513
  fishclient._do_web_request(
476
- fishclient._setbiosurl, redfishsettings, 'PATCH', etag=etag)
514
+ seturl, redfishsettings, 'PATCH', etag=etag)
477
515
 
478
516
  def attach_remote_media(self, url, username, password, vmurls):
479
517
  return None
@@ -481,10 +519,10 @@ class OEMHandler(object):
481
519
  def detach_remote_media(self):
482
520
  return None
483
521
 
484
- def get_description(self):
522
+ def get_description(self, fishclient):
485
523
  return {}
486
524
 
487
- def get_firmware_inventory(self, components):
525
+ def get_firmware_inventory(self, components, fishclient):
488
526
  return []
489
527
 
490
528
  def set_credentials(self, username, password):
@@ -533,6 +571,12 @@ class OEMHandler(object):
533
571
  'Model': self._varsysinfo.get(
534
572
  'SKU', self._varsysinfo.get('PartNumber', '')),
535
573
  }
574
+ if sysinfo['UUID'] and '-' not in sysinfo['UUID']:
575
+ sysinfo['UUID'] = '-'.join((
576
+ sysinfo['UUID'][:8], sysinfo['UUID'][8:12],
577
+ sysinfo['UUID'][12:16], sysinfo['UUID'][16:20],
578
+ sysinfo['UUID'][20:]))
579
+ sysinfo['UUID'] = sysinfo['UUID'].lower()
536
580
  return sysinfo
537
581
  else:
538
582
  for invpair in self.get_inventory():
@@ -548,6 +592,12 @@ class OEMHandler(object):
548
592
  'Model': self._varsysinfo.get(
549
593
  'SKU', self._varsysinfo.get('PartNumber', '')),
550
594
  }
595
+ if sysinfo['UUID'] and '-' not in sysinfo['UUID']:
596
+ sysinfo['UUID'] = '-'.join((
597
+ sysinfo['UUID'][:8], sysinfo['UUID'][8:12],
598
+ sysinfo['UUID'][12:16], sysinfo['UUID'][16:20],
599
+ sysinfo['UUID'][20:]))
600
+ sysinfo['UUID'] = sysinfo['UUID'].lower()
551
601
  yield ('System', sysinfo)
552
602
  self._hwnamemap = {}
553
603
  cpumemurls = []
@@ -599,22 +649,39 @@ class OEMHandler(object):
599
649
  yield (dname, ddata)
600
650
 
601
651
  def _get_adp_inventory(self, onlyname=False, withids=False, urls=None):
652
+ foundmacs = False
653
+ macinfobyadpname = {}
654
+ if 'NetworkInterfaces' in self._varsysinfo:
655
+ nifurls = self._do_web_request(self._varsysinfo['NetworkInterfaces']['@odata.id'])
656
+ nifurls = nifurls.get('Members', [])
657
+ nifurls = [x['@odata.id'] for x in nifurls]
658
+ for nifurl in nifurls:
659
+ nifinfo = self._do_web_request(nifurl)
660
+
661
+ nadurl = nifinfo.get('Links', {}).get('NetworkAdapter', {}).get("@odata.id")
662
+ if nadurl:
663
+ nadinfo = self._do_web_request(nadurl)
664
+ if 'Name' not in nadinfo:
665
+ continue
666
+ nicname = nadinfo['Name']
667
+ yieldinf = {}
668
+ macidx = 1
669
+ for ctrlr in nadinfo.get('Controllers', []):
670
+ porturls = [x['@odata.id'] for x in ctrlr.get(
671
+ 'Links', {}).get('Ports', [])]
672
+ for porturl in porturls:
673
+ portinfo = self._do_web_request(porturl)
674
+ macs = [x for x in portinfo.get(
675
+ 'Ethernet', {}).get(
676
+ 'AssociatedMACAddresses', [])]
677
+ for mac in macs:
678
+ label = 'MAC Address {}'.format(macidx)
679
+ yieldinf[label] = _normalize_mac(mac)
680
+ macidx += 1
681
+ foundmacs = True
682
+ macinfobyadpname[nicname] = yieldinf
602
683
  if not urls:
603
684
  urls = self._get_adp_urls()
604
- if not urls:
605
- # No PCIe device inventory, but *maybe* ethernet inventory...
606
- aidx = 1
607
- for nicinfo in self._get_eth_urls():
608
- nicinfo = self._do_web_request(nicinfo)
609
- nicname = nicinfo.get('Name', None)
610
- nicinfo = nicinfo.get('MACAddress', None)
611
- if not nicname:
612
- nicname = 'NIC'
613
- if nicinfo:
614
- yield (nicname,
615
- {'MAC Address {0}'.format(aidx): nicinfo})
616
- aidx += 1
617
- return
618
685
  for inf in self._do_bulk_requests(urls):
619
686
  adpinfo, url = inf
620
687
  aname = adpinfo.get('Name', 'Unknown')
@@ -636,6 +703,8 @@ class OEMHandler(object):
636
703
  yieldinf = {'Id': adpinfo.get('Id', aname)}
637
704
  else:
638
705
  yieldinf = {}
706
+ if aname in macinfobyadpname:
707
+ yieldinf.update(macinfobyadpname[aname])
639
708
  funurls = [x['@odata.id'] for x in functions]
640
709
  for fun in self._do_bulk_requests(funurls):
641
710
  funinfo, url = fun
@@ -648,14 +717,42 @@ class OEMHandler(object):
648
717
  yieldinf['PCI Subsystem Vendor ID'] = funinfo[
649
718
  'SubsystemVendorId'].replace('0x', '')
650
719
  yieldinf['Type'] = funinfo['DeviceClass']
651
- for nicinfo in funinfo.get('Links', {}).get(
652
- 'EthernetInterfaces', []):
653
- nicinfo = self._do_web_request(nicinfo['@odata.id'])
654
- macaddr = nicinfo.get('MACAddress', None)
655
- if macaddr:
656
- yieldinf['MAC Address {0}'.format(nicidx)] = macaddr
657
- nicidx += 1
720
+ if aname not in macinfobyadpname:
721
+ for nicinfo in funinfo.get('Links', {}).get(
722
+ 'EthernetInterfaces', []):
723
+ nicinfo = self._do_web_request(nicinfo['@odata.id'])
724
+ macaddr = nicinfo.get('MACAddress', None)
725
+ if macaddr:
726
+ macaddr = _normalize_mac(macaddr)
727
+ foundmacs = True
728
+ yieldinf['MAC Address {0}'.format(nicidx)] = macaddr
729
+ nicidx += 1
730
+ if aname in macinfobyadpname:
731
+ del macinfobyadpname[aname]
658
732
  yield aname, yieldinf
733
+ if macinfobyadpname:
734
+ for adp in macinfobyadpname:
735
+ yield adp, macinfobyadpname[adp]
736
+ if not foundmacs:
737
+ # No PCIe device inventory, but *maybe* ethernet inventory...
738
+ idxsbyname = {}
739
+ for nicinfo in self._get_eth_urls():
740
+ nicinfo = self._do_web_request(nicinfo)
741
+ nicname = nicinfo.get('Name', None)
742
+ nicinfo = nicinfo.get('MACAddress', nicinfo.get('PermanentAddress', None))
743
+ if nicinfo and ':' not in nicinfo:
744
+ nicinfo = ':'.join((
745
+ nicinfo[:2], nicinfo[2:4], nicinfo[4:6], nicinfo[6:8],
746
+ nicinfo[8:10], nicinfo[10:12]))
747
+ if not nicname:
748
+ nicname = 'NIC'
749
+ if nicinfo:
750
+ if nicname not in idxsbyname:
751
+ idxsbyname[nicname] = 0
752
+ idxsbyname[nicname] += 1
753
+ nicinfo = nicinfo.lower()
754
+ yield (nicname,
755
+ {'MAC Address {}'.format(idxsbyname[nicname]): nicinfo})
659
756
 
660
757
  def _get_eth_urls(self):
661
758
  ethurls = self._varsysinfo.get('EthernetInterfaces', {})
@@ -780,20 +877,24 @@ class OEMHandler(object):
780
877
 
781
878
  def update_firmware(self, filename, data=None, progress=None, bank=None):
782
879
  usd = self._do_web_request('/redfish/v1/UpdateService')
783
- if usd.get('HttpPushUriTargetsBusy', False):
784
- raise exc.TemporaryError('Cannot run multtiple updates to '
785
- 'same target concurrently')
786
- try:
787
- upurl = usd['HttpPushUri']
788
- except KeyError:
789
- raise exc.UnsupportedFunctionality('Redfish firmware update only supported for implementations with push update support')
790
- if 'HttpPushUriTargetsBusy' in usd:
791
- self._do_web_request(
792
- '/redfish/v1/UpdateService',
793
- {'HttpPushUriTargetsBusy': True}, method='PATCH')
880
+ upurl = usd.get('MultipartHttpPushUri', None)
881
+ ismultipart = True
882
+ if not upurl:
883
+ ismultipart = False
884
+ if usd.get('HttpPushUriTargetsBusy', False):
885
+ raise exc.TemporaryError('Cannot run multtiple updates to '
886
+ 'same target concurrently')
887
+ try:
888
+ upurl = usd['HttpPushUri']
889
+ except KeyError:
890
+ raise exc.UnsupportedFunctionality('Redfish firmware update only supported for implementations with push update support')
891
+ if 'HttpPushUriTargetsBusy' in usd:
892
+ self._do_web_request(
893
+ '/redfish/v1/UpdateService',
894
+ {'HttpPushUriTargetsBusy': True}, method='PATCH')
794
895
  try:
795
896
  uploadthread = webclient.FileUploader(
796
- self.webclient, upurl, filename, data, formwrap=False,
897
+ self.webclient, upurl, filename, data, formwrap=ismultipart,
797
898
  excepterror=False)
798
899
  uploadthread.start()
799
900
  wc = self.webclient
@@ -823,6 +924,7 @@ class OEMHandler(object):
823
924
  # sometimes we get an empty pgress when transitioning from the apply phase to
824
925
  # the validating phase; add a retry here so we don't exit the loop in this case
825
926
  retry = 3
927
+ pct = 0.0
826
928
  while not complete and retry > 0:
827
929
  pgress = self._do_web_request(monitorurl, cache=False)
828
930
  if not pgress:
@@ -837,7 +939,10 @@ class OEMHandler(object):
837
939
  'Suspended'):
838
940
  raise Exception(
839
941
  json.dumps(json.dumps(pgress['Messages'])))
840
- pct = float(pgress['PercentComplete'])
942
+ if 'PercentComplete' in pgress:
943
+ pct = float(pgress['PercentComplete'])
944
+ else:
945
+ print(repr(pgress))
841
946
  complete = state == 'Completed'
842
947
  progress({'phase': phase, 'progress': pct})
843
948
  if complete:
@@ -910,17 +1015,62 @@ class OEMHandler(object):
910
1015
  raise exc.UnsupportedFunctionality(
911
1016
  'Retrieving diagnostic data is not implemented for this platform')
912
1017
 
913
- def get_licenses(self):
914
- raise exc.UnsupportedFunctionality()
1018
+ def _get_license_collection_url(self, fishclient):
1019
+ overview = fishclient._do_web_request('/redfish/v1/')
1020
+ licsrv = overview.get('LicenseService', {}).get('@odata.id', None)
1021
+ if not licsrv:
1022
+ raise exc.UnsupportedFunctionality()
1023
+ lcs = fishclient._do_web_request(licsrv)
1024
+ licenses = lcs.get('Licenses', {}).get('@odata.id',None)
1025
+ if not licenses:
1026
+ raise exc.UnsupportedFunctionality()
1027
+ return licenses
915
1028
 
916
- def delete_license(self, name):
1029
+ def get_extended_bmc_configuration(self, fishclient, hideadvanced=True):
917
1030
  raise exc.UnsupportedFunctionality()
918
1031
 
919
- def save_licenses(self, directory):
920
- raise exc.UnsupportedFunctionality()
1032
+ def _get_licenses(self, fishclient):
1033
+ licenses = self._get_license_collection_url(fishclient)
1034
+ collection = fishclient._do_web_request(licenses)
1035
+ alllic = [x['@odata.id'] for x in collection.get('Members', [])]
1036
+ for license in alllic:
1037
+ licdet = fishclient._do_web_request(license)
1038
+ state = licdet.get('Status', {}).get('State')
1039
+ if state != 'Enabled':
1040
+ continue
1041
+ yield licdet
921
1042
 
922
- def apply_license(self, filename, progress=None, data=None):
923
- raise exc.UnsupportedFunctionality()
1043
+ def get_licenses(self, fishclient):
1044
+ for licdet in self._get_licenses(fishclient):
1045
+ name = licdet['Name']
1046
+ yield {'name': name, 'state': 'Active'}
1047
+
1048
+ def delete_license(self, name, fishclient):
1049
+ for licdet in self._get_licenses(fishclient):
1050
+ lname = licdet['Name']
1051
+ if name == lname:
1052
+ fishclient._do_web_request(licdet['@odata.id'], method='DELETE')
1053
+
1054
+ def save_licenses(self, directory, fishclient):
1055
+ for licdet in self._get_licenses(fishclient):
1056
+ dload = licdet.get('DownloadURI', None)
1057
+ if dload:
1058
+ filename = os.path.basename(dload)
1059
+ savefile = os.path.join(directory, filename)
1060
+ fd = webclient.FileDownloader(fishclient.wc, dload, savefile)
1061
+ fd.start()
1062
+ while fd.isAlive():
1063
+ fd.join(1)
1064
+ yield savefile
1065
+
1066
+ def apply_license(self, filename, fishclient, progress=None, data=None):
1067
+ licenses = self._get_license_collection_url(fishclient)
1068
+ if data is None:
1069
+ data = open(filename, 'rb')
1070
+ licdata = data.read()
1071
+ lic64 = base64.b64encode(licdata).decode()
1072
+ licinfo = {"LicenseString": lic64}
1073
+ fishclient._do_web_request(licenses, licinfo)
924
1074
 
925
1075
  def get_user_expiration(self, uid):
926
1076
  return None