pyghmi 1.6.4__py3-none-any.whl → 1.6.6__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
@@ -842,6 +842,12 @@ class Command(object):
842
842
  fetchdata = fetched['data']
843
843
  if bytearray(fetchdata)[0] != 17:
844
844
  return None
845
+ if param == 0x14:
846
+ vlaninfo = struct.unpack('<H', fetchdata[1:])[0]
847
+ if vlaninfo & 0x8000 == 0x8000:
848
+ return vlaninfo & 0xfff
849
+ else:
850
+ return 'off'
845
851
  if len(fetchdata) == 5: # IPv4 address
846
852
  if prefixlen:
847
853
  return _mask_to_cidr(fetchdata[1:])
@@ -936,7 +942,7 @@ class Command(object):
936
942
  return retdata
937
943
 
938
944
  def set_net_configuration(self, ipv4_address=None, ipv4_configuration=None,
939
- ipv4_gateway=None, channel=None):
945
+ ipv4_gateway=None, vlan_id=None, channel=None):
940
946
  """Set network configuration data.
941
947
 
942
948
  Apply desired network configuration data, leaving unspecified
@@ -950,7 +956,7 @@ class Command(object):
950
956
  :param channel: LAN channel to configure, defaults to autodetect
951
957
  """
952
958
  if (ipv4_address is None and ipv4_configuration is None
953
- and ipv4_gateway is None):
959
+ and ipv4_gateway is None and vlan_id is None):
954
960
  return
955
961
  if channel is None:
956
962
  channel = self.get_network_channel()
@@ -977,6 +983,12 @@ class Command(object):
977
983
  if ipv4_gateway is not None:
978
984
  cmddata = bytearray((channel, 12)) + socket.inet_aton(ipv4_gateway)
979
985
  self.xraw_command(netfn=0xc, command=1, data=cmddata)
986
+ if vlan_id in ('off', 0, '0'):
987
+ cmddata = bytearray((channel, 0x14, 0, 0))
988
+ self.xraw_command(netfn=0xc, command=1, data=cmddata)
989
+ elif vlan_id is not None:
990
+ cmddata = bytearray((channel, 0x14)) + struct.pack('<H', int(vlan_id) | 0x8000)
991
+ self.xraw_command(netfn=0xc, command=1, data=cmddata)
980
992
 
981
993
  def get_storage_configuration(self):
982
994
  """"Get storage configuration data
@@ -1071,6 +1083,7 @@ class Command(object):
1071
1083
  retdata['ipv4_gateway_mac'] = self._fetch_lancfg_param(channel, 13)
1072
1084
  retdata['ipv4_backup_gateway_mac'] = self._fetch_lancfg_param(
1073
1085
  channel, 15)
1086
+ retdata['vlan_id'] = self._fetch_lancfg_param(channel, 0x14)
1074
1087
  self.oem_init()
1075
1088
  self._oem.add_extra_net_configuration(retdata, channel)
1076
1089
  return retdata
@@ -2166,14 +2179,14 @@ class Command(object):
2166
2179
 
2167
2180
  return True
2168
2181
 
2169
- def get_firmware(self, components=()):
2182
+ def get_firmware(self, components=(), category=None):
2170
2183
  """Retrieve OEM Firmware information"""
2171
2184
 
2172
2185
  self.oem_init()
2173
2186
  mcinfo = self.xraw_command(netfn=6, command=1)
2174
2187
  major, minor = struct.unpack('BB', mcinfo['data'][2:4])
2175
2188
  bmcver = '{0}.{1}'.format(major, hex(minor)[2:])
2176
- return self._oem.get_oem_firmware(bmcver, components)
2189
+ return self._oem.get_oem_firmware(bmcver, components, category)
2177
2190
 
2178
2191
  def get_capping_enabled(self):
2179
2192
  """Get PSU based power capping status
@@ -279,7 +279,7 @@ class OEMHandler(object):
279
279
  fru['oem_parser'] = None
280
280
  return fru
281
281
 
282
- def get_oem_firmware(self, bmcver, components):
282
+ def get_oem_firmware(self, bmcver, components, category):
283
283
  """Get Firmware information."""
284
284
 
285
285
  # Here the bmc version is passed into the OEM handler, to allow
@@ -842,7 +842,7 @@ class OEMHandler(generic.OEMHandler):
842
842
  return False
843
843
  return False
844
844
 
845
- def get_oem_firmware(self, bmcver, components):
845
+ def get_oem_firmware(self, bmcver, components, category):
846
846
  if self.has_tsm or self.has_ami or self.has_asrock:
847
847
  command = firmware.get_categories()["firmware"]
848
848
  fw_cmd = self.get_cmd_type("firmware", command)
@@ -865,7 +865,7 @@ class OEMHandler(generic.OEMHandler):
865
865
  bios_versions,
866
866
  self.has_asrock)
867
867
  elif self.has_imm:
868
- return self.immhandler.get_firmware_inventory(bmcver, components)
868
+ return self.immhandler.get_firmware_inventory(bmcver, components, category)
869
869
  elif self.is_fpc:
870
870
  return nextscale.get_fpc_firmware(bmcver, self.ipmicmd,
871
871
  self._fpc_variant)
@@ -556,84 +556,86 @@ class IMMClient(object):
556
556
  def fetch_psu_firmware(self):
557
557
  return []
558
558
 
559
- def fetch_agentless_firmware(self):
559
+ def fetch_agentless_firmware(self, needdisk=True, needadp=True):
560
560
  skipkeys = set([])
561
- cd = self.get_cached_data('lenovo_cached_adapters_fu')
562
- if cd:
563
- adapterdata, fwu = cd
564
- else:
565
- adapterdata = None
566
- if not adapterdata:
567
- if self.updating:
568
- raise pygexc.TemporaryError(
569
- 'Cannot read extended inventory during firmware update')
570
- if self.wc:
571
- adapterdata = self.wc.grab_json_response(
572
- self.ADP_URL, referer=self.adp_referer)
573
- if self.ADP_FU_URL:
574
- fwu = self.wc.grab_json_response(
575
- self.ADP_FU_URL, referer=self.adp_referer)
576
- else:
577
- fwu = {}
578
- if adapterdata:
579
- self.datacache['lenovo_cached_adapters_fu'] = (
580
- (adapterdata, fwu), util._monotonic_time())
581
- if adapterdata and 'items' in adapterdata:
582
- anames = {}
583
- for adata in adapterdata['items']:
584
- aname = adata[self.ADP_NAME]
585
- if aname in anames:
586
- anames[aname] += 1
587
- aname = '{0} {1}'.format(aname, anames[aname])
588
- else:
589
- anames[aname] = 1
590
- donenames = set([])
591
- for fundata in adata[self.ADP_FUN]:
592
- fdata = fundata.get('firmwares', ())
593
- for firm in fdata:
594
- fname = firm['firmwareName'].rstrip()
595
- if '.' in fname:
596
- fname = firm['description'].rstrip()
597
- if fname in donenames:
598
- # ignore redundant entry
599
- continue
600
- if not fname:
601
- continue
602
- donenames.add(fname)
603
- bdata = {}
604
- if 'versionStr' in firm and firm['versionStr']:
605
- bdata['version'] = firm['versionStr']
606
- if ('releaseDate' in firm
607
- and firm['releaseDate']
608
- and firm['releaseDate'] != 'N/A'):
609
- try:
610
- bdata['date'] = self._parse_builddate(
611
- firm['releaseDate'])
612
- except ValueError:
613
- pass
614
- yield '{0} {1}'.format(aname, fname), bdata
615
- for fwi in fwu.get('items', []):
616
- if fwi.get('key', -1) == adata.get('key', -2):
617
- skipkeys.add(fwi['key'])
618
- if fwi.get('fw_status', 0) == 2:
561
+ if needadp:
562
+ cd = self.get_cached_data('lenovo_cached_adapters_fu')
563
+ if cd:
564
+ adapterdata, fwu = cd
565
+ else:
566
+ adapterdata = None
567
+ if not adapterdata:
568
+ if self.updating:
569
+ raise pygexc.TemporaryError(
570
+ 'Cannot read extended inventory during firmware update')
571
+ if self.wc:
572
+ adapterdata = self.wc.grab_json_response(
573
+ self.ADP_URL, referer=self.adp_referer)
574
+ if self.ADP_FU_URL:
575
+ fwu = self.wc.grab_json_response(
576
+ self.ADP_FU_URL, referer=self.adp_referer)
577
+ else:
578
+ fwu = {}
579
+ if adapterdata:
580
+ self.datacache['lenovo_cached_adapters_fu'] = (
581
+ (adapterdata, fwu), util._monotonic_time())
582
+ if adapterdata and 'items' in adapterdata:
583
+ anames = {}
584
+ for adata in adapterdata['items']:
585
+ aname = adata[self.ADP_NAME]
586
+ if aname in anames:
587
+ anames[aname] += 1
588
+ aname = '{0} {1}'.format(aname, anames[aname])
589
+ else:
590
+ anames[aname] = 1
591
+ donenames = set([])
592
+ for fundata in adata[self.ADP_FUN]:
593
+ fdata = fundata.get('firmwares', ())
594
+ for firm in fdata:
595
+ fname = firm['firmwareName'].rstrip()
596
+ if '.' in fname:
597
+ fname = firm['description'].rstrip()
598
+ if fname in donenames:
599
+ # ignore redundant entry
600
+ continue
601
+ if not fname:
602
+ continue
603
+ donenames.add(fname)
619
604
  bdata = {}
620
- if 'fw_pkg_version' in fwi and fwi['fw_pkg_version']:
621
- bdata['version'] = fwi['fw_pkg_version']
622
- elif 'fw_version_pend' in fwi:
623
- bdata['version'] = fwi['fw_version_pend']
624
- yield '{0} Pending Update'.format(aname), bdata
625
- for fwi in fwu.get('items', []):
626
- if fwi.get('key', -1) > 0 and fwi['key'] not in skipkeys:
627
- bdata = {}
628
- bdata['version'] = fwi['fw_version']
629
- yield fwi['adapterName'], bdata
630
- if fwi.get('fw_status', 0) == 2:
605
+ if 'versionStr' in firm and firm['versionStr']:
606
+ bdata['version'] = firm['versionStr']
607
+ if ('releaseDate' in firm
608
+ and firm['releaseDate']
609
+ and firm['releaseDate'] != 'N/A'):
610
+ try:
611
+ bdata['date'] = self._parse_builddate(
612
+ firm['releaseDate'])
613
+ except ValueError:
614
+ pass
615
+ yield '{0} {1}'.format(aname, fname), bdata
616
+ for fwi in fwu.get('items', []):
617
+ if fwi.get('key', -1) == adata.get('key', -2):
618
+ skipkeys.add(fwi['key'])
619
+ if fwi.get('fw_status', 0) == 2:
620
+ bdata = {}
621
+ if 'fw_pkg_version' in fwi and fwi['fw_pkg_version']:
622
+ bdata['version'] = fwi['fw_pkg_version']
623
+ elif 'fw_version_pend' in fwi:
624
+ bdata['version'] = fwi['fw_version_pend']
625
+ yield '{0} Pending Update'.format(aname), bdata
626
+ for fwi in fwu.get('items', []):
627
+ if fwi.get('key', -1) > 0 and fwi['key'] not in skipkeys:
631
628
  bdata = {}
632
- if 'fw_version_pend' in fwi:
633
- bdata['version'] = fwi['fw_version_pend']
634
- yield '{0} Pending Update'.format(fwi['adapterName']), bdata
635
- for disk in self.disk_inventory():
636
- yield disk
629
+ bdata['version'] = fwi['fw_version']
630
+ yield fwi['adapterName'], bdata
631
+ if fwi.get('fw_status', 0) == 2:
632
+ bdata = {}
633
+ if 'fw_version_pend' in fwi:
634
+ bdata['version'] = fwi['fw_version_pend']
635
+ yield '{0} Pending Update'.format(fwi['adapterName']), bdata
636
+ if needdisk:
637
+ for disk in self.disk_inventory():
638
+ yield disk
637
639
  self.weblogout()
638
640
 
639
641
  def disk_inventory(self, mode=0):
@@ -829,11 +831,13 @@ class IMMClient(object):
829
831
  self.weblogout()
830
832
  return hwmap
831
833
 
832
- def get_firmware_inventory(self, bmcver, components):
834
+ def get_firmware_inventory(self, bmcver, components, category):
833
835
  # First we fetch the system firmware found in imm properties
834
836
  # then check for agentless, if agentless, get adapter info using
835
837
  # https, using the caller TLS verification scheme
836
838
  components = set(components)
839
+ if 'core' in components:
840
+ category = 'core'
837
841
  if not components or set(('imm', 'xcc', 'bmc', 'core')) & components:
838
842
  rsp = self.ipmicmd.xraw_command(netfn=0x3a, command=0x50)
839
843
  immverdata = self.parse_imm_buildinfo(rsp['data'])
@@ -1811,12 +1815,16 @@ class XCCClient(IMMClient):
1811
1815
  {'model': psu['model'],
1812
1816
  'version': psu['version']})
1813
1817
 
1814
- def get_firmware_inventory(self, bmcver, components):
1818
+ def get_firmware_inventory(self, bmcver, components, category):
1815
1819
  # First we fetch the system firmware found in imm properties
1816
1820
  # then check for agentless, if agentless, get adapter info using
1817
1821
  # https, using the caller TLS verification scheme
1822
+ if 'core' in components:
1823
+ category = 'core'
1824
+ if not category:
1825
+ category = 'all'
1818
1826
  components = set(components)
1819
- if (not components
1827
+ if category in ('all', 'core') and (not components
1820
1828
  or set(('core', 'imm', 'bmc', 'xcc')) & components):
1821
1829
  rsp = self.ipmicmd.xraw_command(netfn=0x3a, command=0x50)
1822
1830
  immverdata = self.parse_imm_buildinfo(rsp['data'])
@@ -1854,7 +1862,7 @@ class XCCClient(IMMClient):
1854
1862
  'date': '/v2/ibmc/dm/fw/imm3/primary_pending_build_date'})
1855
1863
  if bdata:
1856
1864
  yield '{0} Pending Update'.format(self.bmcname), bdata
1857
- if not components or set(('core', 'uefi', 'bios')) & components:
1865
+ if category in ('all', 'core') and not components or set(('core', 'uefi', 'bios')) & components:
1858
1866
  bdata = self.fetch_grouped_properties({
1859
1867
  'build': '/v2/bios/build_id',
1860
1868
  'version': '/v2/bios/build_version',
@@ -1867,7 +1875,7 @@ class XCCClient(IMMClient):
1867
1875
  'build': '/v2/bios/pending_build_id'})
1868
1876
  if bdata:
1869
1877
  yield 'UEFI Pending Update', bdata
1870
- if not components or set(('lxpm', 'core')) & components:
1878
+ if category in ('all', 'core') and not components or set(('lxpm', 'core')) & components:
1871
1879
  bdata = self.fetch_grouped_properties({
1872
1880
  'build': '/v2/tdm/build_id',
1873
1881
  'version': '/v2/tdm/build_version',
@@ -1888,7 +1896,7 @@ class XCCClient(IMMClient):
1888
1896
  })
1889
1897
  if bdata:
1890
1898
  yield 'LXPM Linux Driver Bundle', bdata
1891
- if not components or set(('lxum', 'core')):
1899
+ if category in ('all', 'core') and (not components or set(('lxum', 'core'))):
1892
1900
  sysinf = self.wc.grab_json_response('/api/dataset/sys_info')
1893
1901
  for item in sysinf.get('items', {}):
1894
1902
  for firm in item.get('firmware', []):
@@ -1899,7 +1907,7 @@ class XCCClient(IMMClient):
1899
1907
  }
1900
1908
  if firm['type'] == 10:
1901
1909
  yield ('LXUM', firminfo)
1902
- if not components or set(('core', 'fpga')) in components:
1910
+ if category in ('all', 'core') and (not components or set(('core', 'fpga')) in components):
1903
1911
  try:
1904
1912
  fpga = self.ipmicmd.xraw_command(netfn=0x3a, command=0x6b,
1905
1913
  data=(0,))
@@ -1909,11 +1917,12 @@ class XCCClient(IMMClient):
1909
1917
  except pygexc.IpmiException as ie:
1910
1918
  if ie.ipmicode != 193:
1911
1919
  raise
1912
- if (not components or components - set((
1913
- 'core', 'uefi', 'bios', 'xcc', 'bmc', 'imm', 'fpga',
1914
- 'lxpm'))):
1915
- for firm in self.fetch_agentless_firmware():
1916
- yield firm
1920
+ needdiskfirmware = category in ('all', 'disks')
1921
+ needadapterfirmware = category in ('all', 'adapters')
1922
+ needpsufirmware = category in ('all', 'misc')
1923
+ for firm in self.fetch_agentless_firmware(needdisk=needdiskfirmware, needadp=needadapterfirmware):
1924
+ yield firm
1925
+ if needpsufirmware:
1917
1926
  for firm in self.fetch_psu_firmware():
1918
1927
  yield firm
1919
1928
 
@@ -2089,13 +2098,14 @@ class XCCClient(IMMClient):
2089
2098
  'progress': 100 * wc.get_upload_progress()})
2090
2099
  if uploadthread.rspstatus >= 300 or uploadthread.rspstatus < 200:
2091
2100
  rsp = uploadthread.rsp
2092
- errmsg = ''
2101
+ errmsg = f'Upload failed with HTTP status {uploadthread.rspstatus}'
2093
2102
  try:
2094
2103
  rsp = json.loads(rsp)
2095
2104
  errmsg = (
2096
2105
  rsp['error']['@Message.ExtendedInfo'][0]['Message'])
2097
2106
  except Exception:
2098
- raise Exception(uploadthread.rsp)
2107
+ errmsg = errmsg + ': ' + str(rsp)
2108
+ raise Exception(errmsg)
2099
2109
  raise Exception(errmsg)
2100
2110
  rsp = json.loads(uploadthread.rsp)
2101
2111
  monitorurl = rsp['@odata.id']
pyghmi/redfish/command.py CHANGED
@@ -188,43 +188,11 @@ class Command(object):
188
188
  self._varsensormap = {}
189
189
  self.powerurl = None
190
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']
191
+ tmpoem = oem.get_oem_handler({}, sysurl, self.wc, self._urlcache, self,
192
+ rootinfo=overview)
193
+ self._varbmcurl = tmpoem.get_default_mgrurl()
201
194
  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))
218
- else:
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
195
+ self.sysurl = tmpoem.get_default_sysurl()
228
196
  self.powerurl = self.sysinfo.get('Actions', {}).get(
229
197
  '#ComputerSystem.Reset', {}).get('target', None)
230
198
 
@@ -271,6 +239,16 @@ class Command(object):
271
239
  okroles.add('ReadOnly')
272
240
  return okroles
273
241
 
242
+ def get_trusted_cas(self):
243
+ for ca in self.oem.get_trusted_cas():
244
+ yield ca
245
+
246
+ def add_trusted_ca(self, pemdata):
247
+ return self.oem.add_trusted_ca(pemdata)
248
+
249
+ def del_trusted_ca(self, certid):
250
+ return self.oem.del_trusted_ca(certid)
251
+
274
252
  def get_users(self):
275
253
  """get list of users and channel access information (helper)
276
254
 
@@ -445,7 +423,7 @@ class Command(object):
445
423
  return self.oem.get_ikvm_launchdata()
446
424
 
447
425
  def user_delete(self, uid):
448
- self.oem.user_delete(uid)
426
+ self.oem.user_delete(uid, self)
449
427
 
450
428
  def set_user_name(self, uid, name):
451
429
  """Set user name
@@ -1026,13 +1004,13 @@ class Command(object):
1026
1004
  self._do_web_request(nicurl, patch, 'PATCH')
1027
1005
 
1028
1006
  def set_net_configuration(self, ipv4_address=None, ipv4_configuration=None,
1029
- ipv4_gateway=None, name=None):
1007
+ ipv4_gateway=None, vlan_id=None, name=None):
1030
1008
  patch = {}
1031
1009
  ipinfo = {}
1032
1010
  dodhcp = None
1033
1011
  netmask = None
1034
1012
  if (ipv4_address is None and ipv4_configuration is None
1035
- and ipv4_gateway is None):
1013
+ and ipv4_gateway is None and vlan_id is None):
1036
1014
  return
1037
1015
  if ipv4_address:
1038
1016
  if '/' in ipv4_address:
@@ -1054,6 +1032,10 @@ class Command(object):
1054
1032
  or 'IPv4StaticAddresses' in patch):
1055
1033
  dodhcp = False
1056
1034
  patch['DHCPv4'] = {'DHCPEnabled': False}
1035
+ if vlan_id in ('off', 0, '0'):
1036
+ patch['VLAN'] = {'VLANEnable': False}
1037
+ elif vlan_id:
1038
+ patch['VLAN'] = {'VLANEnable': True, 'VLANId': int(vlan_id)}
1057
1039
  if patch:
1058
1040
  nicurl = self._get_bmc_nic_url(name)
1059
1041
  try:
@@ -1079,6 +1061,11 @@ class Command(object):
1079
1061
  if gws:
1080
1062
  for gw in gws:
1081
1063
  retdata['static_gateway'] = gw['Address']
1064
+ tagged = netcfg.get('VLAN', {}).get('VLANEnabled', False)
1065
+ if tagged:
1066
+ retdata['vlan_id'] = netcfg.get('VLAN', {}).get('VLANId', None)
1067
+ else:
1068
+ retdata['vlan_id'] = 'off'
1082
1069
  return retdata
1083
1070
 
1084
1071
  def get_net_configuration(self, name=None):
@@ -1101,6 +1088,11 @@ class Command(object):
1101
1088
  hasgateway = _mask_to_cidr(currip['Gateway'])
1102
1089
  retval['ipv4_gateway'] = currip['Gateway'] if hasgateway else None
1103
1090
  retval['ipv4_configuration'] = currip['AddressOrigin']
1091
+ tagged = netcfg.get('VLAN', {}).get('VLANEnable', False)
1092
+ if tagged:
1093
+ retval['vlan_id'] = netcfg.get('VLAN', {}).get('VLANId', None)
1094
+ else:
1095
+ retval['vlan_id'] = 'off'
1104
1096
  return retval
1105
1097
 
1106
1098
  def get_hostname(self):
@@ -1111,10 +1103,10 @@ class Command(object):
1111
1103
  self._do_web_request(self._bmcnicurl,
1112
1104
  {'HostName': hostname}, 'PATCH')
1113
1105
 
1114
- def get_firmware(self, components=()):
1106
+ def get_firmware(self, components=(), category=None):
1115
1107
  self._fwnamemap = {}
1116
1108
  try:
1117
- for firminfo in self.oem.get_firmware_inventory(components, self):
1109
+ for firminfo in self.oem.get_firmware_inventory(components, self, category):
1118
1110
  yield firminfo
1119
1111
  except exc.BypassGenericBehavior:
1120
1112
  return
@@ -15,6 +15,6 @@
15
15
  from pyghmi.redfish.oem.dell import idrac
16
16
 
17
17
 
18
- def get_handler(sysinfo, sysurl, webclient, cache, cmd):
18
+ def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
19
19
  return idrac.OEMHandler(sysinfo, sysurl, webclient, cache,
20
20
  gpool=cmd._gpool)
@@ -23,6 +23,7 @@ from datetime import timedelta
23
23
  from dateutil import tz
24
24
  import socket
25
25
  import time
26
+ import uuid
26
27
 
27
28
  import pyghmi.constants as const
28
29
  import pyghmi.exceptions as exc
@@ -31,6 +32,27 @@ import pyghmi.util.webclient as webclient
31
32
  from pyghmi.util.parse import parse_time
32
33
 
33
34
 
35
+ def _pem_to_dict(pemdata, uefi=False):
36
+ """Pull PEM into a dict
37
+
38
+ Accepts a file-like or a string or bytes.
39
+
40
+ A dict with the PEM as a value for CertificateString is created.
41
+ If uefi, then "UefiSignatureOwner" is also created with a random GUID.
42
+ This is how redfish expects certificate information for CAs to be provided for
43
+ UEFI and for itself.
44
+ """
45
+ if hasattr(pemdata, 'read'):
46
+ pemdata = pemdata.read()
47
+ if isinstance(pemdata, bytes):
48
+ pemdata = pemdata.decode('utf-8')
49
+ cert_dict = {
50
+ 'CertificateString': pemdata,
51
+ 'CertificateType': 'PEM',
52
+ }
53
+ if uefi:
54
+ cert_dict['UefiSignatureOwner'] = str(uuid.uuid4())
55
+ return cert_dict
34
56
 
35
57
  class SensorReading(object):
36
58
  def __init__(self, healthinfo, sensor=None, value=None, units=None,
@@ -188,17 +210,75 @@ class OEMHandler(object):
188
210
  hostnic = None
189
211
  usegenericsensors = True
190
212
 
191
- def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None):
213
+ def _invalidate_url_cache(self, url):
214
+ if url is None:
215
+ return
216
+ if url in self._urlcache:
217
+ del self._urlcache[url]
218
+ if url + '?$expand=.' in self._urlcache:
219
+ del self._urlcache[url + '?$expand=.']
220
+
221
+ def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None, rootinfo={}):
192
222
  self._gpool = gpool
193
223
  self._varsysinfo = sysinfo
194
224
  self._varsysurl = sysurl
225
+ self._varbmcurl = None
195
226
  self._urlcache = cache
196
227
  self.webclient = webclient
197
228
  self._hwnamemap = {}
229
+ self._rootinfo = rootinfo
230
+ if not self._rootinfo:
231
+ self._rootinfo = self.webclient.grab_json_response(
232
+ '/redfish/v1/')
233
+ self._varbmcurl = None
234
+ self._varsysurl = sysurl
198
235
 
199
236
  def get_screenshot(self, outfile):
200
237
  raise exc.UnsupportedFunctionality(
201
238
  'Retrieving screenshot is not implemented for this platform')
239
+
240
+ def get_default_mgrurl(self):
241
+ if not self._varbmcurl and 'Managers' in self._rootinfo:
242
+ bmcoll = self._rootinfo['Managers']['@odata.id']
243
+ res = self.webclient.grab_json_response_with_status(bmcoll)
244
+ if res[1] == 401:
245
+ raise exc.PyghmiException('Access Denied')
246
+ elif res[1] < 200 or res[1] >= 300:
247
+ raise exc.PyghmiException(repr(res[0]))
248
+ bmcs = res[0]['Members']
249
+ if len(bmcs) == 1:
250
+ self._varbmcurl = bmcs[0]['@odata.id']
251
+ return self._varbmcurl
252
+
253
+ def get_default_sysurl(self):
254
+ if not self._varsysurl and 'Systems' in self._rootinfo:
255
+ systems = self._rootinfo['Systems']['@odata.id']
256
+ res = self.webclient.grab_json_response_with_status(systems)
257
+ if res[1] == 401:
258
+ raise exc.PyghmiException('Access Denied')
259
+ elif res[1] < 200 or res[1] >= 300:
260
+ raise exc.PyghmiException(repr(res[0]))
261
+ members = res[0]
262
+ systems = members['Members']
263
+ if self._varsysurl:
264
+ for system in systems:
265
+ if system['@odata.id'] == self._varsysurl or system['@odata.id'].split('/')[-1] == self._varsysurl:
266
+ self._varsysurl = system['@odata.id']
267
+ break
268
+ else:
269
+ raise exc.PyghmiException(
270
+ 'Specified sysurl not found: {0}'.format(self._varsysurl))
271
+ else:
272
+ if len(systems) > 1:
273
+ systems = [x for x in systems if 'DPU' not in x['@odata.id']]
274
+ if len(systems) > 1:
275
+ raise exc.PyghmiException(
276
+ 'Multi system manager, sysurl is required parameter')
277
+ if len(systems):
278
+ self._varsysurl = systems[0]['@odata.id']
279
+ else:
280
+ self._varsysurl = None
281
+ return self._varsysurl
202
282
 
203
283
  def supports_expand(self, url):
204
284
  # Unfortunately, the state of expand in redfish is pretty dicey,
@@ -236,6 +316,68 @@ class OEMHandler(object):
236
316
  cputemps.append(temp)
237
317
  return cputemps
238
318
 
319
+ @property
320
+ def _bmcurl(self):
321
+ if not self._varbmcurl:
322
+ self._varbmcurl = self.sysinfo.get('Links', {}).get(
323
+ 'ManagedBy', [{}])[0].get('@odata.id', None)
324
+ return self._varbmcurl
325
+
326
+ @property
327
+ def sysinfo(self):
328
+ return self._do_web_request(self._varsysurl)
329
+
330
+ def add_trusted_ca(self, pemdata):
331
+ mgrinfo = self._do_web_request(self._bmcurl)
332
+ secpolicy = mgrinfo.get('SecurityPolicy', {}).get('@odata.id', None)
333
+ if secpolicy:
334
+ secinfo = self._do_web_request(secpolicy)
335
+ certcoll = secinfo.get('TLS', {}).get('Client', {}).get('TrustedCertificates', {}).get('@odata.id', None)
336
+ self._invalidate_url_cache(certcoll)
337
+ if certcoll:
338
+ certpayload = _pem_to_dict(pemdata)
339
+ self._do_web_request(certcoll, certpayload)
340
+ return True
341
+ raise exc.PyghmiException('Platform does not support adding trusted CAs')
342
+
343
+ def del_trusted_ca(self, certid):
344
+ mgrinfo = self._do_web_request(self._bmcurl)
345
+ secpolicy = mgrinfo.get('SecurityPolicy', {}).get('@odata.id', None)
346
+ if secpolicy:
347
+ secinfo = self._do_web_request(secpolicy)
348
+ certcoll = secinfo.get('TLS', {}).get('Client', {}).get('TrustedCertificates', {}).get('@odata.id', None)
349
+ if certcoll:
350
+ certs = self._get_expanded_data(certcoll)
351
+ certs = certs.get('Members', [])
352
+ for cert in certs:
353
+ if cert.get('Id', '') == certid:
354
+ self._do_web_request(cert['@odata.id'], method='DELETE')
355
+ self._invalidate_url_cache(certcoll)
356
+ return True
357
+ raise exc.PyghmiException(f'No such certificate found: {certid}')
358
+
359
+ def get_trusted_cas(self):
360
+ mgrinfo = self._do_web_request(self._bmcurl)
361
+ secpolicy = mgrinfo.get('SecurityPolicy', {}).get('@odata.id', None)
362
+ if secpolicy:
363
+ secinfo = self._do_web_request(secpolicy)
364
+ certcoll = secinfo.get('TLS', {}).get('Client', {}).get('TrustedCertificates', {}).get('@odata.id', None)
365
+ if certcoll:
366
+ certs = self._get_expanded_data(certcoll)
367
+ certs = certs.get('Members', [])
368
+ for cert in certs:
369
+ certdesc = {
370
+ 'id': cert.get('Id', ''),
371
+ 'name': cert.get('Name', ''),
372
+ 'pem': cert.get('CertificateString', None),
373
+ 'subject': cert.get('Subject', {}).get('CommonName', ''),
374
+ 'sans': cert.get('Subject', {}).get('AlternativeNames', []),
375
+ 'issuer': cert.get('Issuer', {}).get('CommonName', ''),
376
+ 'validfrom': cert.get('ValidNotBefore', ''),
377
+ 'validto': cert.get('ValidNotAfter', ''),
378
+ }
379
+ yield certdesc
380
+
239
381
  def get_event_log(self, clear=False, fishclient=None, extraurls=[]):
240
382
  bmcinfo = self._do_web_request(fishclient._bmcurl)
241
383
  lsurl = bmcinfo.get('LogServices', {}).get('@odata.id', None)
@@ -257,7 +399,16 @@ class OEMHandler(object):
257
399
  lurls.extend(extraurls)
258
400
  for lurl in lurls:
259
401
  lurl = lurl['@odata.id']
260
- loginfo = self._do_web_request(lurl, cache=(not clear))
402
+ try:
403
+ loginfo = self._do_web_request(lurl, cache=(not clear))
404
+ except Exception:
405
+ record = {}
406
+ record['log_id'] = os.path.basename(lurl)
407
+ record['message'] = 'Could not retrieve log at {0}'.format(lurl)
408
+ record['severity'] = const.Health.Ok
409
+ record['timestamp'] = datetime.now().strftime('%Y-%m-%dT%H:%M:%S')
410
+ yield record
411
+ continue
261
412
  entriesurl = loginfo.get('Entries', {}).get('@odata.id', None)
262
413
  if not entriesurl:
263
414
  continue
@@ -383,13 +534,19 @@ class OEMHandler(object):
383
534
  summary['badreadings'].append(unkinf)
384
535
  return summary
385
536
 
386
- def user_delete(self, uid):
537
+ def user_delete(self, uid, fishclient):
387
538
  # Redfish doesn't do so well with Deleting users either...
388
539
  # Blanking the username seems to be the convention
389
540
  # First, set a bogus password in case the implementation does honor
390
541
  # blank user, at least render such an account harmless
391
- self.set_user_password(uid, base64.b64encode(os.urandom(15)))
392
- self.set_user_name(uid, '')
542
+ try:
543
+ accinfo = fishclient._account_url_info_by_id(uid)
544
+ if not accinfo:
545
+ raise Exception("No such account found")
546
+ self._do_web_request(accinfo[0], method='DELETE')
547
+ except Exception: # fall back to old ipmi-like behavior for such implementations
548
+ fishclient.set_user_password(uid, base64.b64encode(os.urandom(15)))
549
+ fishclient.set_user_name(uid, '')
393
550
  return True
394
551
 
395
552
  def set_bootdev(self, bootdev, persist=False, uefiboot=None,
@@ -661,7 +818,7 @@ class OEMHandler(object):
661
818
  def _extract_fwinfo(self, inf):
662
819
  return {}
663
820
 
664
- def get_firmware_inventory(self, components, fishclient):
821
+ def get_firmware_inventory(self, components, fishclient, category=None):
665
822
  return []
666
823
 
667
824
  def set_credentials(self, username, password):
@@ -1082,14 +1239,15 @@ class OEMHandler(object):
1082
1239
  if (uploadthread.rspstatus >= 300
1083
1240
  or uploadthread.rspstatus < 200):
1084
1241
  rsp = uploadthread.rsp
1085
- errmsg = ''
1242
+ errmsg = f'Update attempt resulted in response status {uploadthread.rspstatus}'
1086
1243
  try:
1087
1244
  rsp = json.loads(rsp)
1088
1245
  errmsg = (
1089
1246
  rsp['error'][
1090
1247
  '@Message.ExtendedInfo'][0]['Message'])
1091
1248
  except Exception:
1092
- raise Exception(uploadthread.rsp)
1249
+ errmsg = errmsg + ': ' + repr(rsp)
1250
+ raise Exception(errmsg)
1093
1251
  raise Exception(errmsg)
1094
1252
  return self.continue_update(uploadthread, progress)
1095
1253
  finally:
@@ -1209,6 +1367,8 @@ class OEMHandler(object):
1209
1367
  res = self._get_cache(url)
1210
1368
  if res:
1211
1369
  return res
1370
+ # If doing a method that may change remote url state, invalidate cache
1371
+ self._invalidate_url_cache(url)
1212
1372
  wc = self.webclient.dupe()
1213
1373
  if etag:
1214
1374
  wc.stdheaders['If-Match'] = etag
@@ -18,19 +18,27 @@ from pyghmi.redfish.oem.lenovo import xcc
18
18
  from pyghmi.redfish.oem.lenovo import xcc3
19
19
  from pyghmi.redfish.oem.lenovo import smm3
20
20
 
21
- def get_handler(sysinfo, sysurl, webclient, cache, cmd):
21
+ def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
22
+ if not sysinfo: # we are before establishing there is one system, and one manager...
23
+ systems, status = webclient.grab_json_response_with_status('/redfish/v1/Systems')
24
+ if status == 200:
25
+ for system in systems.get('Members', []):
26
+ if system.get('@odata.id', '').endswith('/1'):
27
+ sysurl = system['@odata.id']
28
+ sysinfo, status = webclient.grab_json_response_with_status(sysurl)
29
+ break
22
30
  leninf = sysinfo.get('Oem', {}).get('Lenovo', {})
23
31
  mgrinfo = {}
24
32
  if leninf:
25
- mgrinf, status = webclient.grab_json_response_with_status('/redfish/v1/Managers/1')
33
+ mgrinfo, status = webclient.grab_json_response_with_status('/redfish/v1/Managers/1')
26
34
  if status != 200:
27
35
  mgrinfo = {}
28
36
  if not leninf:
29
37
  bmcinfo = cmd.bmcinfo
30
38
  if 'Ami' in bmcinfo.get('Oem', {}):
31
39
  return tsma.TsmHandler(sysinfo, sysurl, webclient, cache)
32
- elif 'xclarity controller' in mgrinf.get('Model', '').lower():
33
- if mgrinf['Model'].endswith('3'):
40
+ elif 'xclarity controller' in mgrinfo.get('Model', '').lower():
41
+ if mgrinfo['Model'].endswith('3'):
34
42
  return xcc3.OEMHandler(sysinfo, sysurl, webclient, cache,
35
43
  gpool=cmd._gpool)
36
44
  else:
@@ -699,33 +699,57 @@ class OEMHandler(generic.OEMHandler):
699
699
  for diskent in adp.get('aimDisks', ()):
700
700
  yield self._get_disk_firmware_single(diskent)
701
701
 
702
- def get_firmware_inventory(self, components, fishclient):
703
- sysinf = self.wc.grab_json_response('/api/dataset/sys_info')
704
- for item in sysinf.get('items', {}):
705
- for firm in item.get('firmware', []):
706
- firminfo = {
707
- 'version': firm['version'],
708
- 'build': firm['build'],
709
- 'date': parse_time(firm['release_date']),
710
- }
711
- if firm['type'] == 5:
712
- yield ('XCC', firminfo)
713
- elif firm['type'] == 6:
714
- yield ('XCC Backup', firminfo)
715
- elif firm['type'] == 0:
716
- yield ('UEFI', firminfo)
717
- elif firm['type'] == 7:
718
- yield ('LXPM', firminfo)
719
- elif firm['type'] == 8:
720
- yield ('LXPM Windows Driver Bundle', firminfo)
721
- elif firm['type'] == 9:
722
- yield ('LXPM Linux Driver Bundle', firminfo)
723
- elif firm['type'] == 10:
724
- yield ('LXUM', firminfo)
725
- for adpinfo in self._get_agentless_firmware(components):
726
- yield adpinfo
727
- for adpinfo in self._get_disk_firmware(components):
728
- yield adpinfo
702
+ def get_firmware_inventory(self, components, fishclient, category=None):
703
+
704
+ if components and 'core' in components:
705
+ category = 'core'
706
+ components = components - set(['core'])
707
+ if not category:
708
+ category = 'all'
709
+ if category in ('all', 'core'):
710
+ sysinf = self.wc.grab_json_response('/api/dataset/sys_info')
711
+ for item in sysinf.get('items', {}):
712
+ for firm in item.get('firmware', []):
713
+ firminfo = {
714
+ 'version': firm['version'],
715
+ 'build': firm['build'],
716
+ 'date': parse_time(firm['release_date']),
717
+ }
718
+ if firm['type'] == 5:
719
+ yield ('XCC', firminfo)
720
+ elif firm['type'] == 6:
721
+ yield ('XCC Backup', firminfo)
722
+ elif firm['type'] == 0:
723
+ yield ('UEFI', firminfo)
724
+ elif firm['type'] == 7:
725
+ yield ('LXPM', firminfo)
726
+ elif firm['type'] == 8:
727
+ yield ('LXPM Windows Driver Bundle', firminfo)
728
+ elif firm['type'] == 9:
729
+ yield ('LXPM Linux Driver Bundle', firminfo)
730
+ elif firm['type'] == 10:
731
+ yield ('LXUM', firminfo)
732
+ if components:
733
+ components = set([x.lower() for x in components])
734
+ components = components - set((
735
+ 'core', 'uefi', 'bios', 'xcc', 'bmc', 'imm', 'fpga',
736
+ 'lxpm'))
737
+ if not components:
738
+ return
739
+ if not components:
740
+ components = set(('all',))
741
+ needadapterfirmware = False
742
+ needdiskfirmware = False
743
+ if category in ('all', 'adapters'):
744
+ needadapterfirmware = True
745
+ if category in ('all', 'disks'):
746
+ needdiskfirmware = True
747
+ if needadapterfirmware:
748
+ for adpinfo in self._get_agentless_firmware(components):
749
+ yield adpinfo
750
+ if needdiskfirmware:
751
+ for adpinfo in self._get_disk_firmware(components):
752
+ yield adpinfo
729
753
  raise pygexc.BypassGenericBehavior()
730
754
 
731
755
  def get_storage_configuration(self, logout=True):
@@ -1296,14 +1320,15 @@ class OEMHandler(generic.OEMHandler):
1296
1320
  if (uploadthread.rspstatus >= 300
1297
1321
  or uploadthread.rspstatus < 200):
1298
1322
  rsp = uploadthread.rsp
1299
- errmsg = ''
1323
+ errmsg = f'Update attempt resulted in response status {uploadthread.rspstatus}'
1300
1324
  try:
1301
1325
  rsp = json.loads(rsp)
1302
1326
  errmsg = (
1303
1327
  rsp['error'][
1304
1328
  '@Message.ExtendedInfo'][0]['Message'])
1305
1329
  except Exception:
1306
- raise Exception(uploadthread.rsp)
1330
+ errmsg = f'Update attempt resulted in response status {uploadthread.rspstatus}: "{repr(rsp)}"'
1331
+ raise Exception(errmsg)
1307
1332
  raise Exception(errmsg)
1308
1333
  rsp = json.loads(uploadthread.rsp)
1309
1334
  monitorurl = rsp['@odata.id']
@@ -1662,7 +1687,7 @@ class OEMHandler(generic.OEMHandler):
1662
1687
  license_errors[rsp['return']])
1663
1688
  return self.get_licenses(fishclient)
1664
1689
 
1665
- def user_delete(self, uid):
1690
+ def user_delete(self, uid, fishclient=None):
1666
1691
  userinfo = self.wc.grab_json_response('/api/dataset/imm_users')
1667
1692
  uidtonamemap = {}
1668
1693
  for user in userinfo.get('items', [{'users': []}])[0].get('users', []):
@@ -128,7 +128,9 @@ class OEMHandler(generic.OEMHandler):
128
128
  anames = {}
129
129
  for adata, _ in adapterdata:
130
130
  skipadapter = False
131
- clabel = adata['Slot']['Location']['PartLocation']['LocationType']
131
+ clabel = adata['Slot']['Location']['PartLocation'].get('LocationType','')
132
+ if not clabel:
133
+ clabel = adata['Slot']['Location']['PartLocation'].get('ServiceLabel', '').split("=")[0]
132
134
  if clabel != 'Embedded':
133
135
  aslot = adata['Slot']['Location']['PartLocation']['LocationOrdinalValue']
134
136
  clabel = 'Slot {0}'.format(aslot)
@@ -240,10 +242,16 @@ class OEMHandler(generic.OEMHandler):
240
242
  standalonedisks = []
241
243
  pools = []
242
244
  for item in rsp.get('Members',[]):
245
+ # Drives shown at 'Direct attached drives' in XCC
246
+ # cannot be used for RAID creation
247
+ if item['Id'].lower() == 'direct_attached_nvme':
248
+ continue
243
249
  cdisks = [item['Drives'][i]['@odata.id'] for i in range(len(item['Drives']))]
244
250
  cid = '{0},{1}'.format(
245
251
  item['Id'],
246
252
  item['StorageControllers'][0]['Location']['PartLocation'].get('LocationOrdinalValue', -1))
253
+ if item['Id'].lower() == 'vroc':
254
+ cid = 'vroc,0'
247
255
  storage_pools = self._get_expanded_data(item['StoragePools']['@odata.id'])
248
256
  for p in storage_pools['Members']:
249
257
  vols = self._get_expanded_data(p['AllocatedVolumes']['@odata.id'])
@@ -1122,7 +1130,7 @@ class OEMHandler(generic.OEMHandler):
1122
1130
  if progress:
1123
1131
  progress({'phase': 'complete'})
1124
1132
 
1125
- def get_firmware_inventory(self, components, fishclient):
1133
+ def get_firmware_inventory(self, components, fishclient, category):
1126
1134
  sfs = fishclient._do_web_request('/api/providers/system_firmware_status')
1127
1135
  pendingscm = sfs.get('fpga_scm_pending_build', None)
1128
1136
  pendinghpm = sfs.get('fpga_hpm_pending_build', None)
@@ -22,18 +22,23 @@ OEMMAP = {
22
22
  }
23
23
 
24
24
 
25
- def get_oem_handler(sysinfo, sysurl, webclient, cache, cmd):
25
+ def get_oem_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}):
26
+ if rootinfo.get('Vendor', None) in OEMMAP:
27
+ return OEMMAP[rootinfo['Vendor']].get_handler(sysinfo, sysurl,
28
+ webclient, cache, cmd, rootinfo)
26
29
  for oem in sysinfo.get('Oem', {}):
27
30
  if oem in OEMMAP:
28
31
  return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
29
- cmd)
32
+ cmd, rootinfo)
30
33
  for oem in sysinfo.get('Links', {}).get('OEM', []):
31
34
  if oem in OEMMAP:
32
35
  return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
33
- cmd)
36
+ cmd, rootinfo)
37
+ if rootinfo: # rootinfo indicates early invocation, bmcinfo not ready yet
38
+ return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)
34
39
  bmcinfo = cmd.bmcinfo
35
40
  for oem in bmcinfo.get('Oem', {}):
36
41
  if oem in OEMMAP:
37
42
  return OEMMAP[oem].get_handler(sysinfo, sysurl, webclient, cache,
38
- cmd)
39
- return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool)
43
+ cmd, rootinfo)
44
+ return generic.OEMHandler(sysinfo, sysurl, webclient, cache, cmd._gpool, rootinfo)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyghmi
3
- Version: 1.6.4
3
+ Version: 1.6.6
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,13 +11,13 @@ pyghmi/cmd/pyghmiutil.py,sha256=hir7FFvwKDNxYGpOPCgIVSgHP4UsVKFIbVBgBWqkBxA,2917
11
11
  pyghmi/cmd/virshbmc.py,sha256=rNbRJrVnx5BmQQLVRV8JK3lW9YWUcP7Z8hCWfpfVLCM,5149
12
12
  pyghmi/ipmi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  pyghmi/ipmi/bmc.py,sha256=LJBF90msq8xFcZSUe3s3jVcW02Ib-0Fc5zWvQNtGmcQ,7283
14
- pyghmi/ipmi/command.py,sha256=hSxL67zvlROMVyUtERc7UDRlJ7Bbk28seGG_RCtuWa0,90945
14
+ pyghmi/ipmi/command.py,sha256=hG5ekt_dExSLrTV6fkr4nFh1U5LylArYdCZolR26DtU,91643
15
15
  pyghmi/ipmi/console.py,sha256=Jle7uJI3ZQS6cMwbEisFEvXjmu5MVqMs17BcAlygR_4,23369
16
16
  pyghmi/ipmi/events.py,sha256=zgUidJIARHomwxasgeYAzDO1AEMfEOzb6XVxzry22Us,22569
17
17
  pyghmi/ipmi/fru.py,sha256=sw5ZBMrEVSBDgOUPVU_ksehQMJvrl2v-r7rVyA9xoiE,14430
18
18
  pyghmi/ipmi/sdr.py,sha256=U4NH-ca1zwEmU_dKT2wvXY2vEvXh33dV9rIUlmf_5Xw,32999
19
19
  pyghmi/ipmi/oem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- pyghmi/ipmi/oem/generic.py,sha256=Ih8BoVebCCmgmHgbO1gJuprecRXG8vi2n4bZ-Oy6r7c,18966
20
+ pyghmi/ipmi/oem/generic.py,sha256=ssNf1IH2_6yPkg38bRePVEwLzue4UUa7l7nsqsyQEP4,18976
21
21
  pyghmi/ipmi/oem/lookup.py,sha256=Ex00OEEolsdWCVhyP0QDGzOxHGEA7sKI8a8fW4kJPD8,3653
22
22
  pyghmi/ipmi/oem/lenovo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  pyghmi/ipmi/oem/lenovo/config.py,sha256=aNN5dvbGlNpWlrrvlWthyAO0PGn6A9Pf75wjhVleu_k,26692
@@ -26,8 +26,8 @@ pyghmi/ipmi/oem/lenovo/dimm.py,sha256=L8k1aBgtvxqyubDBNKdDkz80pDE8Sck1eMLcMz1GhF
26
26
  pyghmi/ipmi/oem/lenovo/drive.py,sha256=MmVgaosEwJXcwi1kKYGnY-dbrx4Zp55941qWMvprUMA,2055
27
27
  pyghmi/ipmi/oem/lenovo/energy.py,sha256=THttIqlwpnj7ljbBWTHjelDLmDaQiCuMvNqJy9Ec7j8,6355
28
28
  pyghmi/ipmi/oem/lenovo/firmware.py,sha256=KS9uUBjFUzvdMw_e-kpr5sYIvFUaeg0yqyo69T94IVc,3747
29
- pyghmi/ipmi/oem/lenovo/handler.py,sha256=ubLUJS9c3yuZiCH1m2I5-NFI9CbpRh3_okijQxARoxM,57236
30
- pyghmi/ipmi/oem/lenovo/imm.py,sha256=MFd26rim8d3yVrKWxXVchaXNch93ACelgDqmjxVZu9s,113461
29
+ pyghmi/ipmi/oem/lenovo/handler.py,sha256=MGfQd08Fd013tH9CwkmfwT7Tkh8BvbdVgeZ4T68GOW8,57256
30
+ pyghmi/ipmi/oem/lenovo/imm.py,sha256=h33_6yNbTgFhhyiBwdWaUOjcpjpQ1gbXK1YPv0DuOC0,114400
31
31
  pyghmi/ipmi/oem/lenovo/inventory.py,sha256=FLJJinw-ibdHtf3KmrTzhWXbQrpxq3TSycVf96Hg7cw,5911
32
32
  pyghmi/ipmi/oem/lenovo/nextscale.py,sha256=bvY7MLOGMioXT7F6HBt5BBftGAEYNo2GkBNjwysPAWs,43734
33
33
  pyghmi/ipmi/oem/lenovo/pci.py,sha256=S7p-5Q2qu2YhlffN-LEmIvjfXim6OlfYL7Q6r6VZqJ4,2020
@@ -43,19 +43,19 @@ pyghmi/ipmi/private/simplesession.py,sha256=cNGaoT0uWIKDut6gUG9kAOX_b_qTzdB26R6I
43
43
  pyghmi/ipmi/private/spd.py,sha256=oEPSXm19X2eNXDiyW_6fVjBFqhuuMAtBI9quRJgclH4,27094
44
44
  pyghmi/ipmi/private/util.py,sha256=ayYodiSydlrrt0_pQppoRB1T6n-KNOiHZSfAlCMcpG0,3847
45
45
  pyghmi/redfish/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- pyghmi/redfish/command.py,sha256=_HLICBri_ON1GqSqqPNWoNKnGz7YBrqHo_l6tjGRiDc,61350
46
+ pyghmi/redfish/command.py,sha256=WyxyJDqYTdnMJiZ4gnYsj9kBSXhXbBIZcLY0Ok72NKY,60849
47
47
  pyghmi/redfish/oem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
48
- pyghmi/redfish/oem/generic.py,sha256=fX6ADqGURX9cQr-Q8cyncQtxLM1oz5CMv57IQ8ijxoY,57313
49
- pyghmi/redfish/oem/lookup.py,sha256=pfJW5xSkUY61OirMeYy0b1SbjBFz6IDfN5ZOYog_Yq4,1530
48
+ pyghmi/redfish/oem/generic.py,sha256=T6DYrH5Tp3v4fzK9tFd3X5UjXp4ljofCGbwm6T88Rpk,64549
49
+ pyghmi/redfish/oem/lookup.py,sha256=9zVqiNMzUS_zwlIp8luZHbN1VTOlyIVQAKnPh1KDf48,1957
50
50
  pyghmi/redfish/oem/dell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  pyghmi/redfish/oem/dell/idrac.py,sha256=pNnmqdV1sOP3ABw0xq0wF1QEO2L8onT7Osc_-sDO8EU,2146
52
- pyghmi/redfish/oem/dell/main.py,sha256=g8773SShUpbYxXB9zVx2pD5z1xP04wB_sXAxcAs6_xY,793
52
+ pyghmi/redfish/oem/dell/main.py,sha256=LnX4p_IOsJNJYbv7p344izj1diLoJZvRLFwCv1aHlAY,806
53
53
  pyghmi/redfish/oem/lenovo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
- pyghmi/redfish/oem/lenovo/main.py,sha256=bnx8LuC_C4_OluNR8JSHIxtSlM4_jdBb4cUzJM6mazE,2597
54
+ pyghmi/redfish/oem/lenovo/main.py,sha256=SLiEHa28NxItqt89SDeM05Em9bJE5dkxNhJ8oCWFtXY,3098
55
55
  pyghmi/redfish/oem/lenovo/smm3.py,sha256=QHasf5QofFuYqtHPeqcwCEJUbd0PV5_C6u-Y8FVdqCk,7598
56
56
  pyghmi/redfish/oem/lenovo/tsma.py,sha256=6GELCuriumARj_kv7fgqtUpo9ekiWHpQcM9v_mnGILI,34645
57
- pyghmi/redfish/oem/lenovo/xcc.py,sha256=78ksNj2-0jquj61lmAZldy3DdcR5KndqbLQ2Y4ZSFOM,84234
58
- pyghmi/redfish/oem/lenovo/xcc3.py,sha256=8LxWzqXCkNmQ9PT-bu2ZSvgziTar3u1J37x8IRBshx8,55843
57
+ pyghmi/redfish/oem/lenovo/xcc.py,sha256=-tLtHv9lPvVSdzNvRhZaG0zFWFJYKv4n0-80gBH_h3E,85401
58
+ pyghmi/redfish/oem/lenovo/xcc3.py,sha256=VV2-apjbjKG806_6Zh05AapdlrLAe-GRs6-3ahVAFHI,56271
59
59
  pyghmi/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  pyghmi/tests/unit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
61
61
  pyghmi/tests/unit/base.py,sha256=xWImA7zPRgfrEe2xAdRZ6w_dLwExGRBJ5CBybssUQGg,744
@@ -64,11 +64,11 @@ pyghmi/tests/unit/ipmi/test_sdr.py,sha256=vb3iLY0cnHJ2K_m4xgYUjEcbPd_ZYhYx-uBowB
64
64
  pyghmi/util/__init__.py,sha256=GZLBWJiun2Plb_VE9dDSh4_PQMCha3gA7QLUqx3oSYI,25
65
65
  pyghmi/util/parse.py,sha256=6VlyBCEcE8gy8PJWmEDdtCyWATaKwPaTswCdioPCWOE,2120
66
66
  pyghmi/util/webclient.py,sha256=i1rJ7NBlPQFTRv7kgzbTaOHsWuPeXcMAjRXm_i8CI0A,15652
67
- pyghmi-1.6.4.dist-info/AUTHORS,sha256=yv4aQom_PII-SNqbeeKrfH-spcG85PRPsZ71Iqjl_fU,2083
68
- pyghmi-1.6.4.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
69
- pyghmi-1.6.4.dist-info/METADATA,sha256=iunjBkI12heq3smej-gMbCPO3KdWK4Y60I6yuxqmwd0,1136
70
- pyghmi-1.6.4.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
71
- pyghmi-1.6.4.dist-info/entry_points.txt,sha256=-OpJliDzATxmuPXK0VR3Ma-Yk_i4ZhfIIB-12A26dSI,168
72
- pyghmi-1.6.4.dist-info/pbr.json,sha256=G84aunNCSzzeuUMkVxkxXAfI8E9qATe2tf_kPWkiFWs,46
73
- pyghmi-1.6.4.dist-info/top_level.txt,sha256=aDtt6S9eVu6-tNdaUs4Pz9PbdUd69bziZZMhNvk9Ulc,7
74
- pyghmi-1.6.4.dist-info/RECORD,,
67
+ pyghmi-1.6.6.dist-info/AUTHORS,sha256=yv4aQom_PII-SNqbeeKrfH-spcG85PRPsZ71Iqjl_fU,2083
68
+ pyghmi-1.6.6.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
69
+ pyghmi-1.6.6.dist-info/METADATA,sha256=iloesXFPEZB8FN0TeZqEecJ7N8cu32uZZJ0YNQLlZRo,1136
70
+ pyghmi-1.6.6.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
71
+ pyghmi-1.6.6.dist-info/entry_points.txt,sha256=-OpJliDzATxmuPXK0VR3Ma-Yk_i4ZhfIIB-12A26dSI,168
72
+ pyghmi-1.6.6.dist-info/pbr.json,sha256=x80AgrPN_697hTcIaJgg-7AmGBFEMVnPZC9dLcdPS_g,46
73
+ pyghmi-1.6.6.dist-info/top_level.txt,sha256=aDtt6S9eVu6-tNdaUs4Pz9PbdUd69bziZZMhNvk9Ulc,7
74
+ pyghmi-1.6.6.dist-info/RECORD,,
@@ -0,0 +1 @@
1
+ {"git_version": "bbebfd9", "is_release": true}
@@ -1 +0,0 @@
1
- {"git_version": "99af22b", "is_release": true}
File without changes