Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pyghmi for openSUSE:Factory checked in at 2025-12-29 15:16:35 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pyghmi (Old) and /work/SRC/openSUSE:Factory/.python-pyghmi.new.1928 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pyghmi" Mon Dec 29 15:16:35 2025 rev:25 rq:1324589 version:1.6.11 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pyghmi/python-pyghmi.changes 2025-11-10 19:18:32.731375936 +0100 +++ /work/SRC/openSUSE:Factory/.python-pyghmi.new.1928/python-pyghmi.changes 2025-12-29 15:17:16.740181049 +0100 @@ -1,0 +2,17 @@ +Sun Dec 28 19:43:20 UTC 2025 - Dirk Müller <[email protected]> + +- update to 1.6.11: + * Remove six dependency + * Bail if number of max RDOC already met + * Change multipart boundary + * Avoid media upload to duplicate target + * Add AMI to oem modules + * Handle systems that model portions as distinct 'Systems' + members + * Add recognition of AMI convention of 'Self' + * Filter out UEFI boot certificates for BMC certificate + candidates + * Manage BMC certificates + * Handle missing dependencies in a redfish dependency map + +------------------------------------------------------------------- Old: ---- pyghmi-1.6.6.tar.gz New: ---- pyghmi-1.6.11.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pyghmi.spec ++++++ --- /var/tmp/diff_new_pack.cZ43xu/_old 2025-12-29 15:17:17.768223275 +0100 +++ /var/tmp/diff_new_pack.cZ43xu/_new 2025-12-29 15:17:17.772223439 +0100 @@ -17,7 +17,7 @@ Name: python-pyghmi -Version: 1.6.6 +Version: 1.6.11 Release: 0 Summary: General Hardware Management Initiative (IPMI and others) License: Apache-2.0 @@ -31,11 +31,9 @@ BuildRequires: %{python_module pip} BuildRequires: %{python_module pytest} BuildRequires: %{python_module python-dateutil >= 2.8.1} -BuildRequires: %{python_module six} BuildRequires: %{python_module wheel} Requires: python-cryptography >= 2.1 Requires: python-python-dateutil >= 2.8.1 -Requires: python-six BuildArch: noarch %if "python%{python_nodots_ver}" == "%{primary_python}" Obsoletes: python3-pyghmi < %{version} ++++++ pyghmi-1.6.6.tar.gz -> pyghmi-1.6.11.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/ChangeLog new/pyghmi-1.6.11/ChangeLog --- old/pyghmi-1.6.6/ChangeLog 2025-10-15 16:51:51.000000000 +0200 +++ new/pyghmi-1.6.11/ChangeLog 2025-12-10 20:10:59.000000000 +0100 @@ -1,6 +1,36 @@ CHANGES ======= +1.6.11 +------ + +* Remove six dependency + +1.6.10 +------ + +* Bail if number of max RDOC already met +* Change multipart boundary + +1.6.9 +----- + +* Avoid media upload to duplicate target + +1.6.8 +----- + +* Add AMI to oem modules +* Handle systems that model portions as distinct 'Systems' members +* Add recognition of AMI convention of 'Self' + +1.6.7 +----- + +* Filter out UEFI boot certificates for BMC certificate candidates +* Manage BMC certificates +* Handle missing dependencies in a redfish dependency map + 1.6.6 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/PKG-INFO new/pyghmi-1.6.11/PKG-INFO --- old/pyghmi-1.6.6/PKG-INFO 2025-10-15 16:51:52.061963000 +0200 +++ new/pyghmi-1.6.11/PKG-INFO 2025-12-10 20:11:00.127628300 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: pyghmi -Version: 1.6.6 +Version: 1.6.11 Summary: Python General Hardware Management Initiative (IPMI and others) Home-page: http://github.com/openstack/pyghmi/ Author: Jarrod Johnson @@ -20,7 +20,15 @@ License-File: LICENSE Requires-Dist: cryptography>=2.1 Requires-Dist: python-dateutil>=2.8.1 -Requires-Dist: six>=1.10.0 +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: requires-dist +Dynamic: summary This is a pure python implementation of IPMI protocol. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/cmd/pyghmicons.py new/pyghmi-1.6.11/pyghmi/cmd/pyghmicons.py --- old/pyghmi-1.6.6/pyghmi/cmd/pyghmicons.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/cmd/pyghmicons.py 2025-12-10 20:10:03.000000000 +0100 @@ -22,8 +22,6 @@ import threading import tty -import six - from pyghmi.ipmi import console @@ -42,7 +40,7 @@ def _print(data): bailout = False - if not isinstance(data, six.string_types): + if not isinstance(data, str): bailout = True data = repr(data) sys.stdout.write(data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/ipmi/oem/lenovo/config.py new/pyghmi-1.6.11/pyghmi/ipmi/oem/lenovo/config.py --- old/pyghmi-1.6.6/pyghmi/ipmi/oem/lenovo/config.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/ipmi/oem/lenovo/config.py 2025-12-10 20:10:03.000000000 +0100 @@ -22,7 +22,6 @@ import random import struct -import six import time import pyghmi.exceptions as pygexc @@ -564,7 +563,7 @@ continue if options[option]['pending'] == options[option]['new_value']: continue - if isinstance(options[option]['new_value'], six.string_types): + if isinstance(options[option]['new_value'], str): # Coerce a simple string parameter to the expected list format options[option]['new_value'] = [options[option]['new_value']] options[option]['pending'] = options[option]['new_value'] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/ipmi/oem/lenovo/imm.py new/pyghmi-1.6.11/pyghmi/ipmi/oem/lenovo/imm.py --- old/pyghmi-1.6.6/pyghmi/ipmi/oem/lenovo/imm.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/ipmi/oem/lenovo/imm.py 2025-12-10 20:10:03.000000000 +0100 @@ -27,7 +27,6 @@ import struct import weakref -import six import zipfile import pyghmi.constants as pygconst @@ -281,7 +280,7 @@ def merge_changeset(self, changeset): for key in changeset: - if isinstance(changeset[key], six.string_types): + if isinstance(changeset[key], str): changeset[key] = {'value': changeset[key]} newvalue = changeset[key]['value'] if self.fwo[key]['is_list'] and not isinstance(newvalue, list): @@ -1124,7 +1123,7 @@ ruleset = {} usbsettings = {} for key in changeset: - if isinstance(changeset[key], six.string_types): + if isinstance(changeset[key], str): changeset[key] = {'value': changeset[key]} currval = changeset[key].get('value', None) if 'smm'.startswith(key.lower()): @@ -1966,15 +1965,29 @@ url = url.replace(':', '') url = 'nfs://' + url yield media.Media(mt['filename'], url) + for rdoc in self._list_rdoc(): + yield rdoc + self.weblogout() + + + def _list_rdoc(self): rt = self.wc.grab_json_response('/api/providers/rp_rdoc_imagelist') if 'items' in rt: for mt in rt['items']: yield media.Media(mt['filename']) - self.weblogout() def upload_media(self, filename, progress=None, data=None): wc = self.wc self._refresh_token() + numrdocs = 0 + for rdoc in self._list_rdoc(): + numrdocs += 1 + if rdoc.name == os.path.basename(filename): + raise pygexc.InvalidParameterValue( + 'An image with that name already exists') + if numrdocs >= 2: + raise pygexc.InvalidParameterValue( + 'Maximum number of uploaded media reached') rsp, statu = wc.grab_json_response_with_status('/rdocupload') newmode = False if statu == 404: @@ -1999,10 +2012,16 @@ progress({'phase': 'upload', 'progress': 100.0 * rsp['received'] / rsp['size']}) self._refresh_token() - rsp = json.loads(uploadthread.rsp) + if uploadthread.rsp: + rsp = json.loads(uploadthread.rsp) + else: + rsp = {} if progress: progress({'phase': 'upload', 'progress': 100.0}) + if 'items' not in rsp or len(rsp['items']) == 0: + errmsg = repr(rsp) if rsp else self.wc.lastjsonerror if self.wc.lastjsonerror else repr(uploadthread.rspstatus) + raise pygexc.PyghmiException('Failed to upload image: ' + errmsg) thepath = rsp['items'][0]['path'] thename = rsp['items'][0]['name'] writeable = 1 if filename.lower().endswith('.img') else 0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/ipmi/oem/lenovo/nextscale.py new/pyghmi-1.6.11/pyghmi/ipmi/oem/lenovo/nextscale.py --- old/pyghmi-1.6.6/pyghmi/ipmi/oem/lenovo/nextscale.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/ipmi/oem/lenovo/nextscale.py 2025-12-10 20:10:03.000000000 +0100 @@ -18,8 +18,6 @@ from xml.etree.ElementTree import fromstring as rfromstring import zipfile -import six - import pyghmi.constants as pygconst import pyghmi.exceptions as pygexc import pyghmi.ipmi.private.session as ipmisession @@ -641,7 +639,7 @@ for key in changeset: if not key: raise pygexc.InvalidParameterValue('Empty key is invalid') - if isinstance(changeset[key], six.string_types): + if isinstance(changeset[key], str): changeset[key] = {'value': changeset[key]} for rule in self.rulemap: if fnmatch.fnmatch(rule, key.lower()): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/ipmi/sdr.py new/pyghmi-1.6.11/pyghmi/ipmi/sdr.py --- old/pyghmi-1.6.6/pyghmi/ipmi/sdr.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/ipmi/sdr.py 2025-12-10 20:10:03.000000000 +0100 @@ -36,8 +36,6 @@ import struct import weakref -import six - import pyghmi.constants as const import pyghmi.exceptions as exc @@ -621,8 +619,7 @@ return "" if ipmitype == 0: # Unicode per 43.15 in ipmi 2.0 spec # the spec is not specific about encoding, assuming utf8 - return six.text_type(struct.pack("%dB" % len(data), *data), - "utf_8") + return struct.pack("%dB" % len(data), *data).decode("utf-8") elif ipmitype == 1: # BCD '+' tmpl = "%02X" * len(data) tstr = tmpl % tuple(data) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/command.py new/pyghmi-1.6.11/pyghmi/redfish/command.py --- old/pyghmi-1.6.6/pyghmi/redfish/command.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/redfish/command.py 2025-12-10 20:10:03.000000000 +0100 @@ -188,6 +188,7 @@ self._varsensormap = {} self.powerurl = None self.sysurl = None + self._initsysurl = sysurl tmpoem = oem.get_oem_handler({}, sysurl, self.wc, self._urlcache, self, rootinfo=overview) self._varbmcurl = tmpoem.get_default_mgrurl() @@ -243,6 +244,14 @@ for ca in self.oem.get_trusted_cas(): yield ca + def get_bmc_csr(self, keytype=None, keylength=None, cn=None, city=None, + state=None, country=None, org=None, orgunit=None): + return self.oem.get_bmc_csr( + keytype=keytype, keylength=keylength, cn=cn) + + def install_bmc_certificate(self, certdata): + return self.oem.install_bmc_certificate(certdata) + def add_trusted_ca(self, pemdata): return self.oem.add_trusted_ca(pemdata) @@ -1220,7 +1229,7 @@ elif self._varbmcurl: self._do_web_request(self._varbmcurl, cache=False) # This is to trigger token validation and renewel self._oem = oem.get_oem_handler( - self.sysinfo, self.sysurl, self.wc, self._urlcache, self) + self.sysinfo, self._initsysurl, self.wc, self._urlcache, self) self._oem.set_credentials(self.username, self.password) return self._oem diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/oem/ami/main.py new/pyghmi-1.6.11/pyghmi/redfish/oem/ami/main.py --- old/pyghmi-1.6.6/pyghmi/redfish/oem/ami/main.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyghmi-1.6.11/pyghmi/redfish/oem/ami/main.py 2025-12-10 20:10:03.000000000 +0100 @@ -0,0 +1,20 @@ +# Copyright 2025 Lenovo Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pyghmi.redfish.oem.ami.megarac as megarac + + +def get_handler(sysinfo, sysurl, webclient, cache, cmd, rootinfo={}): + return megarac.OEMHandler(sysinfo, sysurl, webclient, cache, + gpool=cmd._gpool) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/oem/ami/megarac.py new/pyghmi-1.6.11/pyghmi/redfish/oem/ami/megarac.py --- old/pyghmi-1.6.6/pyghmi/redfish/oem/ami/megarac.py 1970-01-01 01:00:00.000000000 +0100 +++ new/pyghmi-1.6.11/pyghmi/redfish/oem/ami/megarac.py 2025-12-10 20:10:03.000000000 +0100 @@ -0,0 +1,30 @@ +# Copyright 2025 Lenovo Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pyghmi.redfish.oem.generic as generic + + +class OEMHandler(generic.OEMHandler): + + def __init__(self, sysinfo, sysurl, webclient, cache, gpool=None): + super(OEMHandler, self).__init__(sysinfo, sysurl, webclient, cache, + gpool) + if sysurl is None: + systems, status = webclient.grab_json_response_with_status('/redfish/v1/Systems') + if status == 200: + for system in systems.get('Members', []): + if system.get('@odata.id', '').endswith('/Self'): + sysurl = system['@odata.id'] + break + self._varsysurl = sysurl diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/oem/generic.py new/pyghmi-1.6.11/pyghmi/redfish/oem/generic.py --- old/pyghmi-1.6.6/pyghmi/redfish/oem/generic.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/redfish/oem/generic.py 2025-12-10 20:10:03.000000000 +0100 @@ -167,8 +167,11 @@ if currprop == 'CurrentValue': if currattr in self.pend: currval = self.pend[currattr] - else: + elif currattr in self.curr: currval = self.curr[currattr] + else: + break # The cited dependency attribute is missing, can't enforce + # requested override else: currval = self.reg[currattr][currprop] lastcond = self.process(currval, mapfrom, lastcond, lastoper) @@ -232,6 +235,18 @@ '/redfish/v1/') self._varbmcurl = None self._varsysurl = sysurl + self._allsysurls = [] + if sysurl is None: # generic means we need to gather all systems + if 'Systems' in self._rootinfo: + systems = self._rootinfo['Systems']['@odata.id'] + res = self.webclient.grab_json_response_with_status(systems) + if res[1] == 200: + members = res[0]['Members'] + for system in members: + if system['@odata.id'] != sysurl: + self._allsysurls.append(system['@odata.id']) + else: + self._allsysurls = [sysurl] def get_screenshot(self, outfile): raise exc.UnsupportedFunctionality( @@ -327,6 +342,152 @@ def sysinfo(self): return self._do_web_request(self._varsysurl) + def get_bmc_csr(self, keytype=None, keylength=None, cn=None, city=None, + state=None, country=None, org=None, orgunit=None): + # A fun time here, the redfish specification is weird about this. + # We have a certificateservice, sounds good, and an action to generate a CSR, + # straightforward enough, but you have to indicate a certificate collection... + # We get a list of locations, so we have to infer the collection, which + # is perhaps odd, but a relatively safe bet. + # However, the purpose of the certificates is opaque, so we can only guess + # based on strings in the url if there is ambiguity. + rootinfo = self._do_web_request('/redfish/v1/') + certserviceurl = rootinfo.get('CertificateService', {}).get('@odata.id', None) + if not certserviceurl: + raise exc.PyghmiException('No CertificateService found on platform') + certservice = self._do_web_request(certserviceurl) + gencsractinfo = certservice.get('Actions', {}).get("#CertificateService.GenerateCSR", {}) + curveids = gencsractinfo.get('[email protected]', []) + keylens = gencsractinfo.get('[email protected]', []) + keypairalgorithms = gencsractinfo.get('[email protected]', []) + selectedcurve = None + selectedkeylen = None + selectedkpa = None + if not keytype: + for kpa in keypairalgorithms: + if 'ECDH' in kpa: + keytype = 'ECC' + selectedkpa = kpa + break + if 'RSA' in kpa: + selectedkpa = kpa + keytype = 'RSA' + if not keytype: + raise exc.PyghmiException('No valid key type found for CSR generation') + if keytype.upper() in ('ECC', 'ECDSA'): + if not curveids: + raise exc.PyghmiException('No valid curves found for ECC/ECDSA key type') + if keylength: + for curve in curveids: + if fnmatch(curve, '*{0}'.format(keylength)): + selectedcurve = curve + break + else: + selectedcurve = curveids[-1] + elif keytype.upper() == 'RSA': + if not keylens: + raise exc.PyghmiException('No valid key lengths found for RSA key type') + if keylength: + allkeylens = [] + for klp in keylens: + if isinstance(klp, int): + allkeylens.append(klp) + continue + for kl in klp.split(':'): + allkeylens.append(int(kl)) + if keylength not in allkeylens: + raise exc.PyghmiException('Requested key length {0} not supported'.format(keylength)) + selectedkeylen = keylength + gencsrtarg = gencsractinfo.get('target', None) + certcoll = self.get_certificate_collection(certservice) + payload = { + 'CertificateCollection': {"@odata.id": certcoll}, + 'City': city or 'Unspecified', + 'CommonName': cn or self.webclient.thehost, + 'Country': country or 'AQ', # Need *a* valid two letter country code, Antarctica is more equally likely to be wrong than most. + 'Organization': org or 'Unspecified', + 'State': state or 'Unspecified', + } + if orgunit: + payload['OrganizationalUnit'] = orgunit + if selectedcurve: + payload['KeyCurveId'] = selectedcurve + elif selectedkeylen: + payload['KeyLength'] = selectedkeylen + if selectedkpa: + payload['KeyPairAlgorithm'] = selectedkpa + rsp = self._do_web_request(gencsrtarg, payload) + csr = rsp.get('CSRString', None) + return csr + + def get_certificate_collection(self, certservice): + certcollections = set([]) + certlocs = certservice.get('CertificateLocations', {}).get('@odata.id', None) + if certlocs: + certlocdata = self._do_web_request(certlocs) + for cert in certlocdata.get('Links', {}).get('Certificates', []): + certurl = cert.get('@odata.id', None) + if not certurl: + continue + # we need to remove the last part of url to get collection + collurl = '/'.join(certurl.split('/')[:-1]) + certcollections.add(collurl) + if len(certcollections) == 0: + raise exc.PyghmiException('No certificate collections found for certificate operation') + if len(certcollections) > 1: + for candcoll in list(certcollections): + if 'TrustedCertificates' in candcoll: # likely a CA store + certcollections.discard(candcoll) + elif 'LDAP' in candcoll: # certificate for LDAP server + certcollections.discard(candcoll) + elif 'KMIP' in candcoll: # not for TLS + certcollections.discard(candcoll) + elif 'Boot/Certificates' in candcoll: + certcollections.discard(candcoll) + if len(certcollections) > 1: + raise exc.PyghmiException('Multiple certificate collections found, unable to infer intended target for certificate operation') + certcoll = list(certcollections)[0] + return certcoll + + def install_bmc_certificate(self, certdata): + rootinfo = self._do_web_request('/redfish/v1/') + certserviceurl = rootinfo.get('CertificateService', {}).get('@odata.id', None) + if not certserviceurl: + raise exc.PyghmiException('No CertificateService found on platform') + certservice = self._do_web_request(certserviceurl) + certlocs = certservice.get('CertificateLocations', {}).get('@odata.id', None) + if not certlocs: + raise exc.PyghmiException('No CertificateLocations found on platform') + certlocdata = self._do_web_request(certlocs) + allcerts = set([]) + for certloc in certlocdata.get('Links', {}).get('Certificates', []): + certurl = certloc.get('@odata.id', None) + if not certurl: + continue + allcerts.add(certurl) + if len(allcerts) == 0: + raise exc.PyghmiException('No Certificates found on platform') + elif len(allcerts) > 1: + # try to narrow down to server cert + for certurl in list(allcerts): + if 'TrustedCertificates' in certurl: + allcerts.discard(certurl) + elif 'LDAP' in certurl: + allcerts.discard(certurl) + elif 'KMIP' in certurl: + allcerts.discard(certurl) + elif 'Boot/Certificates' in certurl: + allcerts.discard(certurl) + if len(allcerts) > 1: + raise exc.PyghmiException('Multiple Certificates found, unable to infer intended target for certificate installation') + targcerturl = list(allcerts)[0] + replacecerturl = certservice.get('Actions', {}).get( + '#CertificateService.ReplaceCertificate', {}).get('target', None) + certpayload = _pem_to_dict(certdata) + certpayload['CertificateUri'] = {'@odata.id': targcerturl} + #/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate + self._do_web_request(replacecerturl, certpayload) + def add_trusted_ca(self, pemdata): mgrinfo = self._do_web_request(self._bmcurl) secpolicy = mgrinfo.get('SecurityPolicy', {}).get('@odata.id', None) @@ -507,7 +668,7 @@ if memsumstatus != 'OK': dimmfound = False dimmdata = self._get_mem_data() - for dimminfo in dimmdata['Members']: + for dimminfo in dimmdata: if dimminfo.get('Status', {}).get( 'State', None) == 'Absent': continue @@ -1091,8 +1252,7 @@ return urls def _get_cpu_inventory(self, onlynames=False, withids=False, urls=None): - for currcpuinfo in self._get_cpu_data().get( - 'Members', []): + for currcpuinfo in self._get_cpu_data(): url = currcpuinfo['@odata.id'] name = currcpuinfo.get('Name', 'CPU') if name in self._hwnamemap: @@ -1119,18 +1279,21 @@ def _get_cpu_urls(self): md = self._get_cpu_data(False) - return [x['@odata.id'] for x in md.get('Members', [])] + return [x['@odata.id'] for x in md] def _get_cpu_data(self, expand='.'): - cpurl = self._varsysinfo.get('Processors', {}).get('@odata.id', None) - if not cpurl: - return {} - return self._get_expanded_data(cpurl, expand) - + cpumembers = [] + for sysurl in self._allsysurls: + currsysdata = self._do_web_request(sysurl) + currcpuurl = currsysdata.get('Processors', {}).get('@odata.id', None) + if currcpuurl: + currcpudata = self._get_expanded_data(currcpuurl, expand) + cpumembers.extend(currcpudata.get('Members', [])) + return cpumembers def _get_mem_inventory(self, onlyname=False, withids=False, urls=None): memdata = self._get_mem_data() - for currmeminfo in memdata.get('Members', []): # self._do_bulk_requests(urls): + for currmeminfo in memdata: url = currmeminfo['@odata.id'] name = currmeminfo.get('Name', 'Memory') if name in self._hwnamemap: @@ -1161,13 +1324,17 @@ def _get_mem_urls(self): md = self._get_mem_data(False) - return [x['@odata.id'] for x in md.get('Members', [])] + return [x['@odata.id'] for x in md] def _get_mem_data(self, expand='.'): - memurl = self._varsysinfo.get('Memory', {}).get('@odata.id', None) - if not memurl: - return {} - return self._get_expanded_data(memurl, expand) + memmembers = [] + for sysurl in self._allsysurls: + currsysdata = self._do_web_request(sysurl) + currmemurl = currsysdata.get('Memory', {}).get('@odata.id', None) + if currmemurl: + currmemdata = self._get_expanded_data(currmemurl, expand) + memmembers.extend(currmemdata.get('Members', [])) + return memmembers def _get_expanded_data(self, url, expand='.'): topdata = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/oem/lenovo/tsma.py new/pyghmi-1.6.11/pyghmi/redfish/oem/lenovo/tsma.py --- old/pyghmi-1.6.6/pyghmi/redfish/oem/lenovo/tsma.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/redfish/oem/lenovo/tsma.py 2025-12-10 20:10:03.000000000 +0100 @@ -20,8 +20,6 @@ except ImportError: from urllib.parse import urlencode -import six - import pyghmi.exceptions as exc import pyghmi.media as media import pyghmi.redfish.oem.generic as generic @@ -171,7 +169,7 @@ dnschgs = {} wc = self.wc for key in changeset: - if isinstance(changeset[key], six.string_types): + if isinstance(changeset[key], str): changeset[key] = {'value': changeset[key]} currval = changeset[key].get('value', None) if 'dns_servers'.startswith(key.lower()): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/oem/lenovo/xcc.py new/pyghmi-1.6.11/pyghmi/redfish/oem/lenovo/xcc.py --- old/pyghmi-1.6.6/pyghmi/redfish/oem/lenovo/xcc.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/redfish/oem/lenovo/xcc.py 2025-12-10 20:10:03.000000000 +0100 @@ -23,7 +23,6 @@ import socket import time -import six import zipfile import pyghmi.constants as pygconst @@ -261,7 +260,7 @@ def merge_changeset(self, changeset): for key in changeset: - if isinstance(changeset[key], six.string_types): + if isinstance(changeset[key], str): changeset[key] = {'value': changeset[key]} newvalue = changeset[key]['value'] if self.fwo[key]['is_list'] and not isinstance(newvalue, list): @@ -416,7 +415,7 @@ usbsettings = {} secparms = {} for key in changeset: - if isinstance(changeset[key], six.string_types): + if isinstance(changeset[key], str): changeset[key] = {'value': changeset[key]} currval = changeset[key].get('value', None) if key.lower() in self.rulemap: @@ -1187,6 +1186,10 @@ url = url.replace(':', '') url = 'nfs://' + url yield media.Media(mt['filename'], url) + for rdoc in self._list_rdoc(): + yield rdoc + + def _list_rdoc(self): rt = self.wc.grab_json_response('/api/providers/rp_rdoc_imagelist') if 'items' in rt: for mt in rt['items']: @@ -1210,6 +1213,15 @@ def upload_media(self, filename, progress=None, data=None): wc = self.wc self._refresh_token() + numrdocs = 0 + for rdoc in self._list_rdoc(): + numrdocs += 1 + if rdoc.name == os.path.basename(filename): + raise pygexc.InvalidParameterValue( + 'An image with that name already exists') + if numrdocs >= 2: + raise pygexc.InvalidParameterValue( + 'Maximum number of uploaded media reached') rsp, statu = wc.grab_json_response_with_status('/rdocupload') newmode = False if statu == 404: @@ -1234,10 +1246,16 @@ progress({'phase': 'upload', 'progress': 100.0 * rsp['received'] / rsp['size']}) self._refresh_token() - rsp = json.loads(uploadthread.rsp) + if uploadthread.rsp: + rsp = json.loads(uploadthread.rsp) + else: + rsp = {} if progress: progress({'phase': 'upload', 'progress': 100.0}) + if 'items' not in rsp or len(rsp['items']) == 0: + errmsg = repr(rsp) if rsp else self.wc.lastjsonerror if self.wc.lastjsonerror else repr(uploadthread.rspstatus) + raise pygexc.PyghmiException('Failed to upload image: ' + errmsg) thepath = rsp['items'][0]['path'] thename = rsp['items'][0]['name'] writeable = 1 if filename.lower().endswith('.img') else 0 @@ -1249,7 +1267,7 @@ self._refresh_token() if rsp.get('return', -1) != 0: errmsg = repr(rsp) if rsp else self.wc.lastjsonerror - raise Exception('Unrecognized return: ' + errmsg) + raise pygexc.PyghmiException('Failed to upload image: ' + errmsg) ready = False while not ready: time.sleep(3) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/redfish/oem/lookup.py new/pyghmi-1.6.11/pyghmi/redfish/oem/lookup.py --- old/pyghmi-1.6.6/pyghmi/redfish/oem/lookup.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/redfish/oem/lookup.py 2025-12-10 20:10:03.000000000 +0100 @@ -15,10 +15,13 @@ import pyghmi.redfish.oem.dell.main as dell import pyghmi.redfish.oem.generic as generic import pyghmi.redfish.oem.lenovo.main as lenovo +import pyghmi.redfish.oem.ami.main as ami OEMMAP = { 'Lenovo': lenovo, 'Dell': dell, + 'AMI': ami, + 'Ami': ami, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi/util/webclient.py new/pyghmi-1.6.11/pyghmi/util/webclient.py --- old/pyghmi-1.6.6/pyghmi/util/webclient.py 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi/util/webclient.py 2025-12-10 20:10:03.000000000 +0100 @@ -26,8 +26,6 @@ import threading import os -import six - import pyghmi.exceptions as pygexc try: @@ -99,7 +97,7 @@ def get_upload_form(filename, data, formname, otherfields, boundary=BND): if not boundary: - boundary = base64.b64encode(os.urandom(54))[:70] + boundary = base64.urlsafe_b64encode(os.urandom(54))[:66] ffilename = filename.split('/')[-1] if not formname: formname = ffilename @@ -205,7 +203,10 @@ pass plainsock.connect(addrinfo[4]) if self._certverify: - self.sock = ssl.wrap_socket(plainsock, cert_reqs=self.cert_reqs) + ctx = ssl.create_default_context() + ctx.check_hostname = False + ctx.verify_mode = ssl.CERT_NONE + self.sock = ctx.wrap_socket(plainsock) bincert = self.sock.getpeercert(binary_form=True) if not self._certverify(bincert): raise pygexc.UnrecognizedCertificate('Unknown certificate', @@ -303,7 +304,7 @@ """Download a file to filename or file object """ - if isinstance(file, six.string_types): + if isinstance(file, str): file = open(file, 'wb') webclient = self.dupe() dlheaders = self.stdheaders.copy() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi.egg-info/PKG-INFO new/pyghmi-1.6.11/pyghmi.egg-info/PKG-INFO --- old/pyghmi-1.6.6/pyghmi.egg-info/PKG-INFO 2025-10-15 16:51:51.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi.egg-info/PKG-INFO 2025-12-10 20:11:00.000000000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 2.1 +Metadata-Version: 2.4 Name: pyghmi -Version: 1.6.6 +Version: 1.6.11 Summary: Python General Hardware Management Initiative (IPMI and others) Home-page: http://github.com/openstack/pyghmi/ Author: Jarrod Johnson @@ -20,7 +20,15 @@ License-File: LICENSE Requires-Dist: cryptography>=2.1 Requires-Dist: python-dateutil>=2.8.1 -Requires-Dist: six>=1.10.0 +Dynamic: author +Dynamic: author-email +Dynamic: classifier +Dynamic: description +Dynamic: home-page +Dynamic: license +Dynamic: license-file +Dynamic: requires-dist +Dynamic: summary This is a pure python implementation of IPMI protocol. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi.egg-info/SOURCES.txt new/pyghmi-1.6.11/pyghmi.egg-info/SOURCES.txt --- old/pyghmi-1.6.6/pyghmi.egg-info/SOURCES.txt 2025-10-15 16:51:52.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi.egg-info/SOURCES.txt 2025-12-10 20:11:00.000000000 +0100 @@ -85,6 +85,8 @@ pyghmi/redfish/oem/__init__.py pyghmi/redfish/oem/generic.py pyghmi/redfish/oem/lookup.py +pyghmi/redfish/oem/ami/main.py +pyghmi/redfish/oem/ami/megarac.py pyghmi/redfish/oem/dell/__init__.py pyghmi/redfish/oem/dell/idrac.py pyghmi/redfish/oem/dell/main.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi.egg-info/pbr.json new/pyghmi-1.6.11/pyghmi.egg-info/pbr.json --- old/pyghmi-1.6.6/pyghmi.egg-info/pbr.json 2025-10-15 16:51:51.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi.egg-info/pbr.json 2025-12-10 20:11:00.000000000 +0100 @@ -1 +1 @@ -{"git_version": "bbebfd9", "is_release": true} \ No newline at end of file +{"git_version": "8ccf30f", "is_release": true} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/pyghmi.egg-info/requires.txt new/pyghmi-1.6.11/pyghmi.egg-info/requires.txt --- old/pyghmi-1.6.6/pyghmi.egg-info/requires.txt 2025-10-15 16:51:51.000000000 +0200 +++ new/pyghmi-1.6.11/pyghmi.egg-info/requires.txt 2025-12-10 20:11:00.000000000 +0100 @@ -1,3 +1,2 @@ cryptography>=2.1 python-dateutil>=2.8.1 -six>=1.10.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/requirements.txt new/pyghmi-1.6.11/requirements.txt --- old/pyghmi-1.6.6/requirements.txt 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/requirements.txt 2025-12-10 20:10:03.000000000 +0100 @@ -1,5 +1,4 @@ cryptography>=2.1 # BSD/Apache-2.0 python-dateutil>=2.8.1 # BSD -six>=1.10.0 # MIT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pyghmi-1.6.6/setup.py.tmpl new/pyghmi-1.6.11/setup.py.tmpl --- old/pyghmi-1.6.6/setup.py.tmpl 2025-10-15 16:51:03.000000000 +0200 +++ new/pyghmi-1.6.11/setup.py.tmpl 2025-12-10 20:10:03.000000000 +0100 @@ -27,6 +27,6 @@ packages=['pyghmi', 'pyghmi.util', 'pyghmi.ipmi', 'pyghmi.cmd', 'pyghmi.redfish', 'pyghmi.ipmi.private', 'pyghmi.ipmi.oem', 'pyghmi.ipmi.oem.lenovo', 'pyghmi.redfish.oem', - 'pyghmi.redfish.oem.dell', 'pyghmi.redfish.oem.lenovo'], + 'pyghmi.redfish.oem.dell', 'pyghmi.redfish.oem.lenovo', 'pyghmi.redfish.oem.ami'], license='Apache License, Version 2.0')
