diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index 52c329dd..7d46e197 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2013 IBM Corporation # Copyright 2015-2017 Lenovo # @@ -14,15 +12,21 @@ # 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. -# This represents the low layer message framing portion of IPMI + +"""This represents the low layer message framing portion of IPMI""" from itertools import chain +import socket +import struct +import threading + import pyghmi.constants as const import pyghmi.exceptions as exc - import pyghmi.ipmi.events as sel import pyghmi.ipmi.fru as fru from pyghmi.ipmi.oem.lookup import get_oem_handler +import pyghmi.ipmi.private.util as pygutil +from pyghmi.ipmi import sdr try: from pyghmi.ipmi.private import session @@ -32,11 +36,6 @@ try: from pyghmi.ipmi.private import localsession except ImportError: localsession = None -import pyghmi.ipmi.private.util as pygutil -import pyghmi.ipmi.sdr as sdr -import socket -import struct -import threading try: range = xrange @@ -96,6 +95,7 @@ def _mask_to_cidr(mask): def _cidr_to_mask(prefix): return struct.pack('>I', 2 ** prefix - 1 << (32 - prefix)) + class Housekeeper(threading.Thread): """A Maintenance thread for housekeeping @@ -106,6 +106,7 @@ class Housekeeper(threading.Thread): tasks automatically as needed. This is an alternative to calling wait_for_rsp or eventloop in a thread of the callers design. """ + def run(self): Command.eventloop() @@ -130,10 +131,11 @@ class Command(object): :param onlogon: function to run when logon completes in an asynchronous fashion. This will result in a greenthread behavior. :param kg: Optional parameter to use if BMC has a particular Kg configured - :param verifycallback: For OEM extensions that use HTTPS, this function - will be used to evaluate the certificate. - :param keepalive: If False, then an idle connection will logout rather than keepalive - unless held open by console or ongoing activity. + :param verifycallback: For OEM extensions that use HTTPS, this function + will be used to evaluate the certificate. + :param keepalive: If False, then an idle connection will logout rather + than keepalive unless held open by console or ongoing + activity. """ def __init__(self, bmc=None, userid=None, password=None, port=623, @@ -338,7 +340,7 @@ class Command(object): if not isinstance(wait, bool): waitattempts = wait if (wait and - newpowerstate in ('on', 'off', 'shutdown', 'softoff')): + newpowerstate in ('on', 'off', 'shutdown', 'softoff')): if newpowerstate in ('softoff', 'shutdown'): waitpowerstate = 'off' else: @@ -368,8 +370,8 @@ class Command(object): return self._oem.get_video_launchdata() def reset_bmc(self): - """Do a cold reset in BMC - """ + """Do a cold reset in BMC""" + response = self.raw_command(netfn=6, command=2) if 'error' in response: raise exc.IpmiException(response['error']) @@ -626,8 +628,8 @@ class Command(object): for fruid in self._sdr.fru: if self._sdr.fru[fruid].fru_name == component: return self._oem.process_fru(fru.FRU( - ipmicmd=self, fruid=fruid, sdr=self._sdr.fru[fruid]).info, - component) + ipmicmd=self, fruid=fruid, + sdr=self._sdr.fru[fruid]).info, component) return self._oem.get_inventory_of_component(component) def _get_zero_fru(self): @@ -1913,8 +1915,8 @@ class Command(object): return True def get_firmware(self, components=()): - """Retrieve OEM Firmware information - """ + """Retrieve OEM Firmware information""" + self.oem_init() mcinfo = self.xraw_command(netfn=6, command=1) major, minor = struct.unpack('BB', mcinfo['data'][2:4]) @@ -1938,14 +1940,14 @@ class Command(object): return self._oem.set_oem_capping_enabled(enable) def get_remote_kvm_available(self): - """Get remote KVM availability - """ + """Get remote KVM availability""" + self.oem_init() return self._oem.get_oem_remote_kvm_available() def get_domain_name(self): - """Get Domain name - """ + """Get Domain name""" + self.oem_init() return self._oem.get_oem_domain_name() @@ -1962,20 +1964,21 @@ class Command(object): self.oem_init() return self._oem.get_graphical_console() - def update_firmware(self, file, data=None, progress=None, bank=None): + def update_firmware(self, filename, data=None, progress=None, bank=None): """Send file to BMC to perform firmware update - :param filename: The filename to upload to the target BMC - :param data: The payload of the firmware. Default is to read from - specified filename. - :param progress: A callback that will be given a dict describing - update process. Provide if + :param filename: The filename to upload to the target BMC + :param data: The payload of the firmware. Default is to read from + specified filename. + :param progress: A callback that will be given a dict describing + update process. Provide if :param bank: Indicate a target 'bank' of firmware if supported """ + self.oem_init() if progress is None: progress = lambda x: True - return self._oem.update_firmware(file, data, progress, bank) + return self._oem.update_firmware(filename, data, progress, bank) def attach_remote_media(self, url, username=None, password=None): """Attach remote media by url diff --git a/pyghmi/ipmi/console.py b/pyghmi/ipmi/console.py index 7c8e5572..217b12b0 100644 --- a/pyghmi/ipmi/console.py +++ b/pyghmi/ipmi/console.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2014 IBM Corporation # Copyright 2015-2019 Lenovo # @@ -15,12 +13,12 @@ # See the License for the specific language governing permissions and # limitations under the License. # -# This represents the low layer message framing portion of IPMI +"""This represents the low layer message framing portion of IPMI""" -import pyghmi.exceptions as exc import struct import threading +import pyghmi.exceptions as exc from pyghmi.ipmi.private import constants from pyghmi.ipmi.private import session from pyghmi.ipmi.private.util import _monotonic_time @@ -75,8 +73,8 @@ class Console(object): self.callgotsession = None def _got_session(self, response): - """Private function to navigate SOL payload activation - """ + """Private function to navigate SOL payload activation""" + if 'error' in response: self._print_error(response['error']) return @@ -190,15 +188,15 @@ class Console(object): self.pendingoutput[-1] += data def _got_cons_input(self, handle): - """Callback for handle events detected by ipmi session - """ + """Callback for handle events detected by ipmi session""" + self._addpendingdata(handle.read()) if not self.awaitingack: self._sendpendingoutput() def close(self): - """Shut down an SOL session, - """ + """Shut down an SOL session""" + if self.ipmi_session: self.ipmi_session.unregister_keepalive(self.keepaliveid) if self.activated: @@ -331,8 +329,8 @@ class Console(object): self.out_handler(data) def _got_sol_payload(self, payload): - """SOL payload callback - """ + """SOL payload callback""" + # TODO(jbjohnso) test cases to throw some likely scenarios at functions # for example, retry with new data, retry with no new data # retry with unexpected sequence number @@ -457,8 +455,8 @@ class ServerConsole(Console): session.Session.wait_for_rsp(0) def _got_sol_payload(self, payload): - """SOL payload callback - """ + """SOL payload callback""" + # TODO(jbjohnso) test cases to throw some likely scenarios at functions # for example, retry with new data, retry with no new data # retry with unexpected sequence number @@ -540,6 +538,6 @@ class ServerConsole(Console): needskeepalive=needskeepalive) def close(self): - """Shut down an SOL session, - """ + """Shut down an SOL session""" + self.activated = False diff --git a/pyghmi/ipmi/events.py b/pyghmi/ipmi/events.py index fde1b93c..3b83bbf5 100644 --- a/pyghmi/ipmi/events.py +++ b/pyghmi/ipmi/events.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2016 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# __author__ = 'jjohnson2@lenovo.com' +import struct +import time import pyghmi.constants as pygconst import pyghmi.exceptions as pygexc import pyghmi.ipmi.private.constants as ipmiconst -import struct -import time try: range = xrange diff --git a/pyghmi/ipmi/fru.py b/pyghmi/ipmi/fru.py index 75a073be..0b132510 100644 --- a/pyghmi/ipmi/fru.py +++ b/pyghmi/ipmi/fru.py @@ -1,6 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# coding=utf8 - # Copyright 2015 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,27 +12,31 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This module provides access to SDR offered by a BMC -# This data is common between 'sensors' and 'inventory' modules since SDR -# is both used to enumerate sensors for sensor commands and FRU ids for FRU -# commands +"""This module provides access to SDR offered by a BMC -# For now, we will not offer persistent SDR caching as we do in xCAT's IPMI -# code. Will see if it is adequate to advocate for high object reuse in a -# persistent process for the moment. +This data is common between 'sensors' and 'inventory' modules since SDR +is both used to enumerate sensors for sensor commands and FRU ids for FRU +commands -# Focus is at least initially on the aspects that make the most sense for a -# remote client to care about. For example, smbus information is being -# skipped for now +For now, we will not offer persistent SDR caching as we do in xCAT's IPMI +code. Will see if it is adequate to advocate for high object reuse in a +persistent process for the moment. -# This file handles parsing of fru format records as presented by IPMI -# devices. This format is documented in the 'Platform Management FRU -# Information Storage Definition (Document Revision 1.2) +Focus is at least initially on the aspects that make the most sense for a +remote client to care about. For example, smbus information is being +skipped for now + +This file handles parsing of fru format records as presented by IPMI +devices. This format is documented in the 'Platform Management FRU +Information Storage Definition (Document Revision 1.2) +""" + +import struct +import time import pyghmi.exceptions as iexc import pyghmi.ipmi.private.spd as spd -import struct -import time + fruepoch = 820454400 # 1/1/1996, 0:00 @@ -242,7 +243,7 @@ class FRU(object): # removing trailing spaces and nulls like makes sense for text # and rely on vendors to workaround deviations in their OEM # module - #retinfo = retinfo.rstrip(b'\x00 ') + # retinfo = retinfo.rstrip(b'\x00 ') return retinfo, newoffset elif currtype == 1: # BCD 'plus' retdata = '' diff --git a/pyghmi/ipmi/oem/generic.py b/pyghmi/ipmi/oem/generic.py index 3e82073a..a5bd96a1 100644 --- a/pyghmi/ipmi/oem/generic.py +++ b/pyghmi/ipmi/oem/generic.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2015 Lenovo Corporation # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -190,8 +188,8 @@ class OEMHandler(object): return fru def get_oem_firmware(self, bmcver, components): - """Get Firmware information. - """ + """Get Firmware information.""" + # Here the bmc version is passed into the OEM handler, to allow # the handler to enrich the data. For the generic case, just # provide the generic BMC version, which is all that is possible @@ -201,7 +199,7 @@ class OEMHandler(object): # code to know whether it cares or not. The main purpose of the # components argument is to indicate when certain performance # optimizations can be performed. - yield ('BMC Version', {'version': bmcver}) + yield 'BMC Version', {'version': bmcver} def get_oem_capping_enabled(self): """Get PSU based power capping status @@ -218,13 +216,11 @@ class OEMHandler(object): return () def get_oem_remote_kvm_available(self): - """Get remote KVM availability - """ + """Get remote KVM availability""" return False def get_oem_domain_name(self): - """Get Domain name - """ + """Get Domain name""" return () def set_oem_domain_name(self, name): @@ -309,15 +305,11 @@ class OEMHandler(object): return [] def set_hostname(self, hostname): - """OEM specific hook to specify name information - - """ + """OEM specific hook to specify name information""" raise exc.UnsupportedFunctionality() def get_hostname(self): - """OEM specific hook to specify name information - - """ + """OEM specific hook to specify name information""" raise exc.UnsupportedFunctionality() def set_user_access(self, uid, channel, callback, link_auth, ipmi_msg, @@ -343,7 +335,8 @@ class OEMHandler(object): return {} def set_bmc_configuration(self, changeset): - raise exc.UnsupportedFunctionality('Platform does not support setting bmc attributes') + raise exc.UnsupportedFunctionality( + 'Platform does not support setting bmc attributes') def get_system_configuration(self, hideadvanced): """Retrieve system configuration diff --git a/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py b/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py index d21356d2..f6281580 100644 --- a/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py +++ b/pyghmi/ipmi/oem/lenovo/EfiDecompressor.py @@ -1,4 +1,15 @@ -# -*- coding: utf-8 -*- +# 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 struct try: @@ -6,7 +17,8 @@ try: except NameError: pass -class BitArray: + +class BitArray(object): def __init__(self, data): self._Data = bytearray(data) diff --git a/pyghmi/ipmi/oem/lenovo/config.py b/pyghmi/ipmi/oem/lenovo/config.py index de5ccdd9..40ed5670 100644 --- a/pyghmi/ipmi/oem/lenovo/config.py +++ b/pyghmi/ipmi/oem/lenovo/config.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2017-2019 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,21 +12,22 @@ # See the License for the specific language governing permissions and # limitations under the License. -# from Matthew Garret's 'firmware_config' project. +"""from Matthew Garret's 'firmware_config' project. -# This contains functions to manage the firmware configuration of Lenovo -# servers +This contains functions to manage the firmware configuration of Lenovo servers +""" import ast -import struct import random -import pyghmi.exceptions as pygexc +import struct +import pyghmi.exceptions as pygexc from pyghmi.ipmi.oem.lenovo import EfiDecompressor +import six try: - from lxml import etree import EfiCompressor + from lxml import etree except ImportError: etree = None EfiCompressor = None @@ -168,7 +167,7 @@ class LenovoFirmwareConfig(object): data.append(0) while retries: - retries = retries-1 + retries = retries - 1 response = run_command_with_retry(self.connection, data=data) try: if response['code'] == 0 or retries == 0: @@ -207,7 +206,7 @@ class LenovoFirmwareConfig(object): amount = remaining else: amount = blocksize - data.extend(inputdata[offset:offset+amount]) + data.extend(inputdata[offset:offset + amount]) remaining -= blocksize offset += blocksize run_command_with_retry(self.connection, data=data) @@ -378,24 +377,25 @@ class LenovoFirmwareConfig(object): instidx = 1 for inst in current: optname = '{0}.{1}'.format(optionname, instidx) - options[optname] = dict(current=inst, - default=default, - possible=possible, - pending=None, - new_value=None, - help=help, - is_list=is_list, - lenovo_value=lenovo_value, - lenovo_id=lenovo_id, - lenovo_group=lenovo_group, - lenovo_setting=lenovo_setting, - lenovo_reboot=reset, - lenovo_protect=protect, - lenovo_instance=instidx, - readonly_expression=readonly, - hide_expression=hide, - sortid=sortid, - alias=alias) + options[optname] = dict( + current=inst, + default=default, + possible=possible, + pending=None, + new_value=None, + help=help, + is_list=is_list, + lenovo_value=lenovo_value, + lenovo_id=lenovo_id, + lenovo_group=lenovo_group, + lenovo_setting=lenovo_setting, + lenovo_reboot=reset, + lenovo_protect=protect, + lenovo_instance=instidx, + readonly_expression=readonly, + hide_expression=hide, + sortid=sortid, + alias=alias) sortid += 1 instidx += 1 continue @@ -405,24 +405,25 @@ class LenovoFirmwareConfig(object): for currid in sorted(instancetochoicemap): optname = '{0}.{1}'.format(optionname, currid) current = instancetochoicemap[currid] - options[optname] = dict(current=current, - default=default, - possible=possible, - pending=None, - new_value=None, - help=help, - is_list=is_list, - lenovo_value=lenovo_value, - lenovo_id=lenovo_id, - lenovo_group=lenovo_group, - lenovo_setting=lenovo_setting, - lenovo_reboot=reset, - lenovo_protect=protect, - lenovo_instance=currid, - readonly_expression=readonly, - hide_expression=hide, - sortid=sortid, - alias=alias) + options[optname] = dict( + current=current, + default=default, + possible=possible, + pending=None, + new_value=None, + help=help, + is_list=is_list, + lenovo_value=lenovo_value, + lenovo_id=lenovo_id, + lenovo_group=lenovo_group, + lenovo_setting=lenovo_setting, + lenovo_reboot=reset, + lenovo_protect=protect, + lenovo_instance=currid, + readonly_expression=readonly, + hide_expression=hide, + sortid=sortid, + alias=alias) sortid += 1 continue lenovoinstance = "" @@ -481,8 +482,7 @@ class LenovoFirmwareConfig(object): ','.join(sorted(options[option]['readonly_why']))) errstr += ea raise pygexc.InvalidParameterValue(errstr) - if (isinstance(options[option]['new_value'], str) or - isinstance(options[option]['new_value'], unicode)): + if isinstance(options[option]['new_value'], six.string_types): # 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'] @@ -495,7 +495,8 @@ class LenovoFirmwareConfig(object): setting = etree.Element('setting', ID=options[option]['lenovo_setting']) if options[option]['lenovo_group'] is not None: - group = etree.Element('group', ID=options[option]['lenovo_group']) + group = etree.Element('group', + ID=options[option]['lenovo_group']) config.append(group) group.append(setting) else: diff --git a/pyghmi/ipmi/oem/lenovo/cpu.py b/pyghmi/ipmi/oem/lenovo/cpu.py index 4228ac57..fe334307 100755 --- a/pyghmi/ipmi/oem/lenovo/cpu.py +++ b/pyghmi/ipmi/oem/lenovo/cpu.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2015 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,24 +12,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pyghmi.ipmi.oem.lenovo.inventory import EntryField, \ - parse_inventory_category_entry +from pyghmi.ipmi.oem.lenovo import inventory cpu_fields = ( - EntryField("index", "B"), - EntryField("Cores", "B"), - EntryField("Threads", "B"), - EntryField("Manufacturer", "13s"), - EntryField("Family", "30s"), - EntryField("Model", "30s"), - EntryField("Stepping", "3s"), - EntryField("Maximum Frequency", "I", - valuefunc=lambda v: hex(v)[2:]), - EntryField("model", "21s"), - EntryField("reserved", "h", include=False) + inventory.EntryField("index", "B"), + inventory.EntryField("manufacture_location", "B"), + inventory.EntryField("channel_number", "B"), + inventory.EntryField("module_type", "10s"), + inventory.EntryField("ddr_voltage", "10s"), + inventory.EntryField("speed", "I", + valuefunc=lambda v: hex(v)[2:]), + inventory.EntryField("model", "21s"), + inventory.EntryField("reserved", "h", include=False) ) def parse_dimm_info(raw): - return parse_inventory_category_entry(raw, dimm_fields) + return inventory.parse_inventory_category_entry(raw, dimm_fields) def get_categories(): diff --git a/pyghmi/ipmi/oem/lenovo/drive.py b/pyghmi/ipmi/oem/lenovo/drive.py index ad70101f..acc5db3b 100755 --- a/pyghmi/ipmi/oem/lenovo/drive.py +++ b/pyghmi/ipmi/oem/lenovo/drive.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2015 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,49 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pyghmi.ipmi.oem.lenovo.inventory import EntryField, \ - parse_inventory_category_entry +from pyghmi.ipmi.oem.lenovo import inventory drive_fields = ( - EntryField("index", "B"), - EntryField("VendorID", "64s"), - EntryField("Size", "I", - valuefunc=lambda v: str(v) + " MB"), - EntryField("MediaType", "B", mapper={ + inventory.EntryField("index", "B"), + inventory.EntryField("VendorID", "64s"), + inventory.EntryField("Size", "I", + valuefunc=lambda v: str(v) + " MB"), + inventory.EntryField("MediaType", "B", mapper={ 0x00: "HDD", 0x01: "SSD" }), - EntryField("InterfaceType", "B", mapper={ + inventory.EntryField("InterfaceType", "B", mapper={ 0x00: "Unknown", 0x01: "ParallelSCSI", 0x02: "SAS", 0x03: "SATA", 0x04: "FC" }), - EntryField("FormFactor", "B", mapper={ + inventory.EntryField("FormFactor", "B", mapper={ 0x00: "Unknown", 0x01: "2.5in", 0x02: "3.5in" }), - EntryField("LinkSpeed", "B", mapper={ + inventory.EntryField("LinkSpeed", "B", mapper={ 0x00: "Unknown", 0x01: "1.5 Gb/s", 0x02: "3.0 Gb/s", 0x03: "6.0 Gb/s", 0x04: "12.0 Gb/s" }), - EntryField("SlotNumber", "B"), - EntryField("DeviceState", "B", mapper={ + inventory.EntryField("SlotNumber", "B"), + inventory.EntryField("DeviceState", "B", mapper={ 0x00: "active", 0x01: "stopped", 0xff: "transitioning" }), # There seems to be an undocumented byte at the end - EntryField("Reserved", "B", include=False)) + inventory.EntryField("Reserved", "B", include=False)) def parse_drive_info(raw): - return parse_inventory_category_entry(raw, drive_fields) + return inventory.parse_inventory_category_entry(raw, drive_fields) def get_categories(): diff --git a/pyghmi/ipmi/oem/lenovo/energy.py b/pyghmi/ipmi/oem/lenovo/energy.py index f7af5a7a..72eb6fe4 100644 --- a/pyghmi/ipmi/oem/lenovo/energy.py +++ b/pyghmi/ipmi/oem/lenovo/energy.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2017 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pyghmi.exceptions as pygexc import struct +import pyghmi.exceptions as pygexc + class EnergyManager(object): diff --git a/pyghmi/ipmi/oem/lenovo/firmware.py b/pyghmi/ipmi/oem/lenovo/firmware.py index 2feb4263..cab717e5 100644 --- a/pyghmi/ipmi/oem/lenovo/firmware.py +++ b/pyghmi/ipmi/oem/lenovo/firmware.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2015 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,33 +12,34 @@ # See the License for the specific language governing permissions and # limitations under the License. -from pyghmi.ipmi.oem.lenovo.inventory import EntryField, \ - parse_inventory_category_entry +from pyghmi.ipmi.oem.lenovo import inventory + firmware_fields = ( - EntryField("Revision", "B"), - EntryField("Bios", "16s"), - EntryField("Operational ME", "10s"), - EntryField("Recovery ME", "10s"), - EntryField("RAID 1", "16s"), - EntryField("RAID 2", "16s"), - EntryField("Mezz 1", "16s"), - EntryField("Mezz 2", "16s"), - EntryField("BMC", "16s"), - EntryField("LEPT", "16s"), - EntryField("PSU 1", "16s"), - EntryField("PSU 2", "16s"), - EntryField("CPLD", "16s"), - EntryField("LIND", "16s"), - EntryField("WIND", "16s"), - EntryField("DIAG", "16s")) + inventory.EntryField("Revision", "B"), + inventory.EntryField("Bios", "16s"), + inventory.EntryField("Operational ME", "10s"), + inventory.EntryField("Recovery ME", "10s"), + inventory.EntryField("RAID 1", "16s"), + inventory.EntryField("RAID 2", "16s"), + inventory.EntryField("Mezz 1", "16s"), + inventory.EntryField("Mezz 2", "16s"), + inventory.EntryField("BMC", "16s"), + inventory.EntryField("LEPT", "16s"), + inventory.EntryField("PSU 1", "16s"), + inventory.EntryField("PSU 2", "16s"), + inventory.EntryField("CPLD", "16s"), + inventory.EntryField("LIND", "16s"), + inventory.EntryField("WIND", "16s"), + inventory.EntryField("DIAG", "16s")) def parse_firmware_info(raw): - bytes_read, data = parse_inventory_category_entry(raw, firmware_fields) + bytes_read, data = inventory.parse_inventory_category_entry( + raw, firmware_fields) del data['Revision'] for key in data: - yield(key, {'version': data[key]}) + yield key, {'version': data[key]} def get_categories(): diff --git a/pyghmi/ipmi/oem/lenovo/handler.py b/pyghmi/ipmi/oem/lenovo/handler.py index 63de168f..5c50d57b 100755 --- a/pyghmi/ipmi/oem/lenovo/handler.py +++ b/pyghmi/ipmi/oem/lenovo/handler.py @@ -1,5 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - # Copyright 2015-2017 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,22 +14,17 @@ import base64 import binascii +import socket +import struct import traceback -try: - from urllib import urlencode -except ImportError: - from urllib.parse import urlencode +import weakref import pyghmi.constants as pygconst import pyghmi.exceptions as pygexc import pyghmi.ipmi.oem.generic as generic -import pyghmi.ipmi.private.constants as ipmiconst -import pyghmi.ipmi.private.util as util - from pyghmi.ipmi.oem.lenovo import cpu from pyghmi.ipmi.oem.lenovo import dimm from pyghmi.ipmi.oem.lenovo import drive - from pyghmi.ipmi.oem.lenovo import firmware from pyghmi.ipmi.oem.lenovo import imm from pyghmi.ipmi.oem.lenovo import inventory @@ -40,15 +33,17 @@ from pyghmi.ipmi.oem.lenovo import pci from pyghmi.ipmi.oem.lenovo import psu from pyghmi.ipmi.oem.lenovo import raid_controller from pyghmi.ipmi.oem.lenovo import raid_drive -from pyghmi.redfish.oem.lenovo import tsma +import pyghmi.ipmi.private.constants as ipmiconst +import pyghmi.ipmi.private.util as util import pyghmi.redfish.command as redfishcmd - - +from pyghmi.redfish.oem.lenovo import tsma import pyghmi.util.webclient as wc -import socket -import struct -import weakref +try: + from urllib import urlencode +except ImportError: + from urllib.parse import urlencode + try: range = xrange except NameError: @@ -162,10 +157,11 @@ class OEMHandler(generic.OEMHandler): elif self.is_fpc: self.smmhandler = nextscale.SMMClient(ipmicmd) elif self.has_tsma: - conn = wc.SecureHTTPConnection(ipmicmd.bmc, 443, + conn = wc.SecureHTTPConnection( + ipmicmd.bmc, 443, verifycallback=self.ipmicmd.certverify) - #sysinfo, sysurl, webclient, cache=None): - self.tsmahandler = tsma.TsmHandler(None, None, conn, fish=redfishcmd) + self.tsmahandler = tsma.TsmHandler(None, None, conn, + fish=redfishcmd) self.tsmahandler.set_credentials( ipmicmd.ipmi_session.userid.decode('utf-8'), ipmicmd.ipmi_session.password.decode('utf-8')) @@ -254,12 +250,12 @@ class OEMHandler(generic.OEMHandler): event['severity'] = pygconst.Health.Ok event['component'] = 'User Privilege' event['component_type'] = ipmiconst.sensor_type_codes[6] - event['event_data'] = \ - 'User {0} on channel {1} had privilege changed ' \ + event['event_data'] = ( + 'User {0} on channel {1} had privilege changed ' 'from {2} to {3}'.format( oemdata[2], oemdata[1], oemdata[3] & 0b1111, - (oemdata[3] & 0b11110000) >> 4 - ) + (oemdata[3] & 0b11110000) >> 4) + ) else: event['event'] = 'OEM event: {0}'.format( ' '.join(format(x, '02x') for x in event['oemdata'])) @@ -342,13 +338,13 @@ class OEMHandler(generic.OEMHandler): def set_user_access(self, uid, channel, callback, link_auth, ipmi_msg, privilege_level): - if self.is_fpc and self._fpc_variant == 2: + if self.is_fpc and self._fpc_variant == 2: self.smmhandler.set_user_priv(uid, privilege_level) @property def is_fpc(self): - """True if the target is a Lenovo nextscale fan power controller - """ + """True if the target is a Lenovo nextscale fan power controller""" + if self.has_imm or self.has_xcc: return None if self._fpc_variant is not None: @@ -378,8 +374,8 @@ class OEMHandler(generic.OEMHandler): @property def has_tsm(self): - """True if this particular server have a TSM based service processor - """ + """True if this particular server have a TSM based service processor""" + if (self.oemid['manufacturer_id'] == 19046 and self.oemid['device_id'] == 32): try: @@ -559,7 +555,7 @@ class OEMHandler(generic.OEMHandler): endidx = len(macs) - 5 macprefix = None while idx < endidx: - currmac = macs[idx:idx+6] + currmac = macs[idx:idx + 6] if not isinstance(currmac, bytearray): # invalid vpd format, abort attempts to extract # mac in this way @@ -638,16 +634,20 @@ class OEMHandler(generic.OEMHandler): return nextscale.get_fpc_firmware(bmcver, self.ipmicmd, self._fpc_variant) elif self.has_tsma: - return self.tsmahandler.get_firmware_inventory(components, raisebypass=False) + return self.tsmahandler.get_firmware_inventory(components, + raisebypass=False) return super(OEMHandler, self).get_oem_firmware(bmcver, components) def get_diagnostic_data(self, savefile, progress, autosuffix=False): if self.has_xcc: - return self.immhandler.get_diagnostic_data(savefile, progress, autosuffix) + return self.immhandler.get_diagnostic_data(savefile, progress, + autosuffix) if self.is_fpc: - return self.smmhandler.get_diagnostic_data(savefile, progress, autosuffix) + return self.smmhandler.get_diagnostic_data(savefile, progress, + autosuffix) if self.has_tsma: - return self.tsmahandler.get_diagnostic_data(savefile, progress, autosuffix) + return self.tsmahandler.get_diagnostic_data(savefile, progress, + autosuffix) def get_oem_capping_enabled(self): if self.has_tsm: @@ -706,9 +706,9 @@ class OEMHandler(generic.OEMHandler): # set the domain name content name = name.ljust(256, "\x00") for i in range(0, 4): - data = [4, i+1] - offset = i*64 - data.extend([ord(x) for x in name[offset:offset+64]]) + data = [4, i + 1] + offset = i * 64 + data.extend([ord(x) for x in name[offset:offset + 64]]) self.ipmicmd.xraw_command(netfn=0x32, command=0x6c, data=data) self._restart_dns() @@ -799,7 +799,7 @@ class OEMHandler(generic.OEMHandler): else: # fall back to a dumber, but more universal formatter ipv6str = binascii.b2a_hex(ipv6_addr) - ipv6str = ':'.join([ipv6str[x:x+4] for x in range(0, 32, 4)]) + ipv6str = ':'.join([ipv6str[x:x + 4] for x in range(0, 32, 4)]) netdata['ipv6_addresses'] = [ '{0}/{1}'.format(ipv6str, ipv6_prefix)] @@ -858,7 +858,7 @@ class OEMHandler(generic.OEMHandler): data=(1, selector, 0, 1)) # now do the set for x in range(0, 256, 64): - currdata = padded[x:x+64] + currdata = padded[x:x + 64] currchunk = x // 64 + 1 cmddata = [1, selector, currchunk] + currdata self.ipmicmd.xraw_command(netfn=0x32, command=0x9f, data=cmddata) @@ -872,7 +872,7 @@ class OEMHandler(generic.OEMHandler): imgnames = rsp['data'][1:] shortnames = [] for idx in range(0, len(imgnames), 22): - shortnames.append(imgnames[idx+2:idx+22].rstrip('\0')) + shortnames.append(imgnames[idx + 2:idx + 22].rstrip('\0')) return shortnames def _megarac_media_waitforready(self, imagename): diff --git a/pyghmi/ipmi/oem/lenovo/imm.py b/pyghmi/ipmi/oem/lenovo/imm.py index d6376eff..c177626a 100644 --- a/pyghmi/ipmi/oem/lenovo/imm.py +++ b/pyghmi/ipmi/oem/lenovo/imm.py @@ -1,6 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# coding=utf8 - # Copyright 2016-2019 Lenovo # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,25 +20,28 @@ import fnmatch import json import math import os.path +import random +import re +import socket +import struct +import weakref + import pyghmi.constants as pygconst import pyghmi.exceptions as pygexc import pyghmi.ipmi.oem.lenovo.config as config import pyghmi.ipmi.oem.lenovo.energy as energy import pyghmi.ipmi.private.session as ipmisession import pyghmi.ipmi.private.util as util -import pyghmi.ipmi.sdr as sdr +from pyghmi.ipmi import sdr import pyghmi.media as media import pyghmi.storage as storage import pyghmi.util.webclient as webclient -import random -import re -import socket -import struct +import six + try: from urllib import urlencode except ImportError: from urllib.parse import urlencode -import weakref numregex = re.compile('([0-9]+)') @@ -55,10 +55,11 @@ funtypes = { 12: 'Fabric Controller', } + def naturalize_string(key): """Analyzes string in a human way to enable natural sort - :param nodename: The node name to analyze + :param key: string for the split :returns: A structure that can be consumed by 'sorted' """ return [int(text) if text.isdigit() else text.lower() @@ -185,7 +186,8 @@ class IMMClient(object): try: self.fwo = self.fwc.get_fw_options() except Exception: - raise Exception(self.bmcname + ' failed to retrieve UEFI configuration') + raise Exception(self.bmcname + + ' failed to retrieve UEFI configuration') self.fwovintage = util._monotonic_time() retcfg = {} for opt in self.fwo: @@ -229,8 +231,7 @@ class IMMClient(object): raise pygexc.InvalidParameterValue( '{0} not a known setting'.format(key)) for key in changeset: - if (isinstance(changeset[key], str) or - isinstance(changeset[key], unicode)): + if isinstance(changeset[key], six.string_types): changeset[key] = {'value': changeset[key]} newvalue = changeset[key]['value'] if self.fwo[key]['is_list'] and not isinstance(newvalue, list): @@ -244,12 +245,12 @@ class IMMClient(object): newvalues = [newvalue] newnewvalues = [] for newvalue in newvalues: - newv = re.sub('\s+', ' ', newvalue) + newv = re.sub(r'\s+', ' ', newvalue) if (self.fwo[key]['possible'] and newvalue not in self.fwo[key]['possible']): candlist = [] for candidate in self.fwo[key]['possible']: - candid = re.sub('\s+', ' ', candidate) + candid = re.sub(r'\s+', ' ', candidate) if newv.lower().startswith(candid.lower()): newvalue = candidate break @@ -277,7 +278,8 @@ class IMMClient(object): raise def clear_bmc_configuration(self): - self.ipmicmd.xraw_command(0x2e, 0xcc, data=(0x5e, 0x2b, 0, 0xa, 1, 0xff, 0, 0, 0)) + self.ipmicmd.xraw_command(0x2e, 0xcc, + data=(0x5e, 0x2b, 0, 0xa, 1, 0xff, 0, 0, 0)) def set_property(self, propname, value): if not isinstance(value, int) or value > 255: @@ -285,7 +287,7 @@ class IMMClient(object): propname = propname.encode('utf-8') proplen = len(propname) | 0b10000000 valuelen = 0x11 # The value is always one byte, for now - cmdlen = len(propname) + 4 # the flags byte, two tlv bytes, and value + cmdlen = len(propname) + 4 # the flags byte, two tlv bytes, and value cdata = bytearray([3, 0, cmdlen, 1, proplen]) + propname cdata += bytearray([valuelen, value]) rsp = self.ipmicmd.xraw_command(netfn=0x3a, command=0xc4, data=cdata) @@ -324,8 +326,7 @@ class IMMClient(object): return None adata = urlencode({'user': self.username, 'password': self.password, - 'SessionTimeout': 60 - }) + 'SessionTimeout': 60}) headers = {'Connection': 'keep-alive', 'Referer': 'https://{0}/designs/imm/index.php'.format( self.imm), @@ -369,7 +370,6 @@ class IMMClient(object): if returnit: return retdata - def grab_cacheable_json(self, url, age=30): data = self.get_cached_data(url, age) if not data: @@ -427,7 +427,7 @@ class IMMClient(object): progress({'phase': 'complete'}) def attach_remote_media(self, url, user, password): - url = url.replace(':', '\:') + url = url.replace(':', '\\:') params = urlencode({ 'RP_VmAllocateMountUrl({0},{1},1,,)'.format( self.username, url): '' @@ -466,7 +466,7 @@ class IMMClient(object): '/data/set', 'RP_RemoveFile({0}, 0)'.format( uload['slotId'])) for url in removeurls: - url = url.replace(':', '\:') + url = url.replace(':', '\\:') params = urlencode({ 'RP_VmAllocateUnMountUrl({0},{1},0,)'.format( self.username, url): ''}) @@ -496,8 +496,8 @@ class IMMClient(object): adapterdata = self.wc.grab_json_response( self.ADP_URL, referer=self.adp_referer) if self.ADP_FU_URL: - fwu = self.wc.grab_json_response(self.ADP_FU_URL, - referer=self.adp_referer) + fwu = self.wc.grab_json_response( + self.ADP_FU_URL, referer=self.adp_referer) else: fwu = {} if adapterdata: @@ -549,7 +549,7 @@ class IMMClient(object): self.weblogout() def disk_inventory(self, mode=0): - if mode==1: + if mode == 1: # Bypass IMM hardware inventory for now return storagedata = self.get_cached_data('lenovo_cached_storage') @@ -647,7 +647,7 @@ class IMMClient(object): 'Model': fixup_str(model), 'Serial': fixup_str(serial), } - for disk in self.disk_inventory(mode=1): # hardware mode + for disk in self.disk_inventory(mode=1): # hardware mode hwmap[disk[0]] = disk[1] adapterdata = self.get_cached_data('lenovo_cached_adapters') if not adapterdata: @@ -716,7 +716,7 @@ class IMMClient(object): for lp in portinfo['logicalPorts']: ma = lp['networkAddr'] ma = ':'.join( - [ma[i:i+2] for i in range( + [ma[i:i + 2] for i in range( 0, len(ma), 2)]).lower() bdata['MAC Address {0}'.format( portinfo['portIndex'])] = ma @@ -827,13 +827,20 @@ class XCCClient(IMMClient): settings = {} passrules = self.wc.grab_json_response('/api/dataset/imm_users_global') passrules = passrules.get('items', [{}])[0] - settings['password_reuse_count'] = {'value': passrules.get('pass_min_resuse')} - settings['password_change_interval'] = {'value': passrules.get('pass_change_interval')} - settings['password_expiration'] = {'value': passrules.get('pass_expire_days')} - settings['password_login_failures'] = {'value': passrules.get('max_login_failures')} - settings['password_complexity'] = {'value': passrules.get('pass_complex_required')} - settings['password_min_length'] = {'value': passrules.get('pass_min_length')} - settings['password_lockout_period'] = {'value': passrules.get('lockout_period')} + settings['password_reuse_count'] = { + 'value': passrules.get('pass_min_resuse')} + settings['password_change_interval'] = { + 'value': passrules.get('pass_change_interval')} + settings['password_expiration'] = { + 'value': passrules.get('pass_expire_days')} + settings['password_login_failures'] = { + 'value': passrules.get('max_login_failures')} + settings['password_complexity'] = { + 'value': passrules.get('pass_complex_required')} + settings['password_min_length'] = { + 'value': passrules.get('pass_min_length')} + settings['password_lockout_period'] = { + 'value': passrules.get('lockout_period')} try: enclosureinfo = self.ipmicmd.xraw_command(0x3a, 0xf1, data=[0]) except pygexc.IpmiException: @@ -841,7 +848,8 @@ class XCCClient(IMMClient): settings['smm'] = { 'default': 'Disable', 'possible': ['Enable', 'Disable'], - 'help': 'Enables or disables the network of the D2 enclosure manager.', + 'help': 'Enables or disables the network of the D2 ' + 'enclosure manager.', } if bytearray(enclosureinfo['data'])[0] == 2: settings['smm']['value'] = 'Disable' @@ -864,8 +872,7 @@ class XCCClient(IMMClient): def set_bmc_configuration(self, changeset): ruleset = {} for key in changeset: - if (isinstance(changeset[key], str) or - isinstance(changeset[key], unicode)): + if isinstance(changeset[key], six.string_types): changeset[key] = {'value': changeset[key]} currval = changeset[key].get('value', None) if 'smm'.startswith(key.lower()): @@ -880,17 +887,21 @@ class XCCClient(IMMClient): ruleset['USER_GlobalPassExpWarningPeriod'] = warntime else: raise pygexc.InvalidParameterValue( - '{0} not a known setting'.format(key)) + '{0} not a known setting'.format(key)) if ruleset: - rsp = self.wc.grab_json_response('/api/dataset', ruleset) + self.wc.grab_json_response('/api/dataset', ruleset) def clear_system_configuration(self): res = self.wc.grab_json_response_with_status( - '/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios', - {'Action': 'Bios.ResetBios'}, - headers={'Authorization': 'Basic ' + base64.b64encode( - self.username + ':' + self.password), - 'Content-Type': 'application/json'}) + '/redfish/v1/Systems/1/Bios/Actions/Bios.ResetBios', + {'Action': 'Bios.ResetBios'}, + headers={ + 'Authorization': 'Basic ' + + base64.b64encode( + self.username + ':' + self.password), + 'Content-Type': 'application/json' + } + ) if res[1] < 200 or res[1] >= 300: raise Exception( 'Unexpected response to clear configuration: {0}'.format( @@ -995,21 +1006,21 @@ class XCCClient(IMMClient): if storagedata and 'items' in storagedata: for adp in storagedata['items']: for diskent in adp.get('disks', ()): - if mode==0: + if mode == 0: yield self.get_disk_firmware(diskent) - elif mode==1: + elif mode == 1: yield self.get_disk_hardware(diskent) for diskent in adp.get('aimDisks', ()): - if mode==0: + if mode == 0: yield self.get_disk_firmware(diskent) - elif mode==1: + elif mode == 1: yield self.get_disk_hardware(diskent) if mode == 1: bdata = {'Description': 'Unmanaged Disk'} if adp.get('m2Type', -1) == 2: - yield ('M.2 Disk', bdata) + yield 'M.2 Disk', bdata for umd in adp.get('unmanagedDisks', []): - yield ('Disk {0}'.format(umd['slotNo']), bdata) + yield 'Disk {0}'.format(umd['slotNo']), bdata def get_disk_hardware(self, diskent, prefix=''): bdata = {} @@ -1084,7 +1095,8 @@ class XCCClient(IMMClient): 'perspan': drivesperspan, } else: - pass # TODO: adding new volume to existing array would be here + # TODO(): adding new volume to existing array would be here + pass def _make_jbod(self, disk, realcfg): currstatus = self._get_status(disk, realcfg) @@ -1252,7 +1264,8 @@ class XCCClient(IMMClient): def get_oem_sensor_names(self, ipmicmd): oemsensornames = super(XCCClient, self).get_oem_sensor_names(ipmicmd) - therminfo = self.grab_cacheable_json('/api/dataset/pwrmgmt?params=GetThermalRealTimeData', 1) + therminfo = self.grab_cacheable_json( + '/api/dataset/pwrmgmt?params=GetThermalRealTimeData', 1) if therminfo: for name in sorted(therminfo['items'][0]): if 'DIMM' in name and 'Temp' in name: @@ -1260,9 +1273,10 @@ class XCCClient(IMMClient): return oemsensornames def get_oem_sensor_descriptions(self, ipmicmd): - oemdesc = [{'name': x, 'type': 'Energy' - } for x in super(XCCClient, self).get_oem_sensor_names(ipmicmd)] - therminfo = self.grab_cacheable_json('/api/dataset/pwrmgmt?params=GetThermalRealTimeData', 1) + oemdesc = [{'name': x, 'type': 'Energy'} for x in super( + XCCClient, self).get_oem_sensor_names(ipmicmd)] + therminfo = self.grab_cacheable_json( + '/api/dataset/pwrmgmt?params=GetThermalRealTimeData', 1) if therminfo: for name in sorted(therminfo['items'][0]): if 'DIMM' in name and 'Temp' in name: @@ -1272,7 +1286,8 @@ class XCCClient(IMMClient): def get_oem_sensor_reading(self, name, ipmicmd): if 'Energy' in name: return super(XCCClient, self).get_oem_sensor_reading(name, ipmicmd) - therminfo = self.grab_cacheable_json('/api/dataset/pwrmgmt?params=GetThermalRealTimeData', 1) + therminfo = self.grab_cacheable_json( + '/api/dataset/pwrmgmt?params=GetThermalRealTimeData', 1) temp = therminfo.get('items', [{}])[0].get(name, None) if temp is None: raise pygexc.UnsupportedFunctionality('No sunch sensor ' + name) @@ -1387,10 +1402,8 @@ class XCCClient(IMMClient): return for psu in psudata.get('items', ()): yield ('PSU {0}'.format(psu['slot']), - { - 'model': psu['model'], - 'version': psu['version'], - }) + {'model': psu['model'], + 'version': psu['version']}) def get_firmware_inventory(self, bmcver, components): # First we fetch the system firmware found in imm properties @@ -1432,47 +1445,48 @@ class XCCClient(IMMClient): 'version': '/v2/ibmc/dm/fw/imm3/primary_pending_build_version', 'date': '/v2/ibmc/dm/fw/imm3/primary_pending_build_date'}) if bdata: - yield ('{0} Pending Update'.format(self.bmcname), bdata) - if (not components or set(('core', 'uefi', 'bios')) & components): + yield '{0} Pending Update'.format(self.bmcname), bdata + if not components or set(('core', 'uefi', 'bios')) & components: bdata = self.fetch_grouped_properties({ 'build': '/v2/bios/build_id', 'version': '/v2/bios/build_version', 'date': '/v2/bios/build_date'}) if bdata: - yield ('UEFI', bdata) + yield 'UEFI', bdata # Note that the next pending could be pending for either primary # or backup, so can't promise where it will go bdata = self.fetch_grouped_properties({ 'build': '/v2/bios/pending_build_id'}) if bdata: - yield ('UEFI Pending Update', bdata) + yield 'UEFI Pending Update', bdata if not components or set(('lxpm', 'core')) & components: bdata = self.fetch_grouped_properties({ 'build': '/v2/tdm/build_id', 'version': '/v2/tdm/build_version', 'date': '/v2/tdm/build_date'}) if bdata: - yield ('LXPM', bdata) + yield 'LXPM', bdata bdata = self.fetch_grouped_properties({ 'build': '/v2/drvwn/build_id', 'version': '/v2/drvwn/build_version', 'date': '/v2/drvwn/build_date', }) if bdata: - yield ('LXPM Windows Driver Bundle', bdata) + yield 'LXPM Windows Driver Bundle', bdata bdata = self.fetch_grouped_properties({ 'build': '/v2/drvln/build_id', 'version': '/v2/drvln/build_version', 'date': '/v2/drvln/build_date', }) if bdata: - yield ('LXPM Linux Driver Bundle', bdata) + yield 'LXPM Linux Driver Bundle', bdata if not components or set(('core', 'fpga')) in components: try: fpga = self.ipmicmd.xraw_command(netfn=0x3a, command=0x6b, data=(0,)) - fpga = '{0}.{1}.{2}'.format(*struct.unpack('BBB', fpga['data'])) - yield ('FPGA', {'version': fpga}) + fpga = '{0}.{1}.{2}'.format( + *struct.unpack('BBB', fpga['data'])) + yield 'FPGA', {'version': fpga} except pygexc.IpmiException as ie: if ie.ipmicode != 193: raise @@ -1633,7 +1647,8 @@ class XCCClient(IMMClient): progress({'phase': 'upload', 'progress': 100.0 * rsp['received'] / rsp['size']}) elif rsp['state'] != 'done': - if rsp.get('status', None) == 413 or uploadthread.rspstatus == 413: + if (rsp.get('status', None) == 413 or + uploadthread.rspstatus == 413): raise Exception('File is larger than supported') raise Exception('Unexpected result:' + repr(rsp)) uploadstate = rsp['state'] @@ -1669,7 +1684,8 @@ class XCCClient(IMMClient): elif rsp.get('return', -1) == 108: raise Exception('Temporary error validating update, try again') elif rsp.get('return', -1) == 109: - raise Exception('Invalid update file or component does not support remote update') + raise Exception('Invalid update file or component does ' + 'not support remote update') elif rsp.get('return', -1) != 0: errmsg = repr(rsp) if rsp else self.wc.lastjsonerror raise Exception('Unexpected return to verify: ' + errmsg) @@ -1680,12 +1696,13 @@ class XCCClient(IMMClient): rsp, status = self.wc.grab_json_response_with_status( '/api/providers/fwupdate', json.dumps({'UPD_WebVerifyUploadFileStatus': 1})) - if not rsp or status != 200 or rsp.get('return', -1) == 2: + if not rsp or status != 200 or rsp.get('return', -1) == 2: # The XCC firmware predates the FileStatus api verifyuploadfilersp = rsp break if rsp.get('return', -1) == 109: - raise Exception('Invalid update file or component does not support remote update') + raise Exception('Invalid update file or component does ' + 'not support remote update') if rsp.get('return', -1) != 0: errmsg = repr(rsp) if rsp else self.wc.lastjsonerror raise Exception( @@ -1714,7 +1731,7 @@ class XCCClient(IMMClient): 'TDM', 'WINDOWS DRIV', 'LINUX DRIVER', 'UEFI', 'IMM'): # adapter firmware webid = rsp['items'][0]['webfile_build_id'] - locations = webid[webid.find('[')+1:webid.find(']')] + locations = webid[webid.find('[') + 1:webid.find(']')] locations = locations.split(':') validselectors = set([]) for loc in locations: @@ -1801,7 +1818,8 @@ class XCCClient(IMMClient): def augment_psu_info(self, info, psuname): psud = self.get_cached_data('lenovo_cached_psuhwinfo') if not psud: - psud = self.wc.grab_json_response('/api/dataset/imm_power_supplies') + psud = self.wc.grab_json_response( + '/api/dataset/imm_power_supplies') if not psud: return self.datacache['lenovo_cached_psuhwinfo'] = ( @@ -1818,7 +1836,7 @@ class XCCClient(IMMClient): except (socket.timeout, socket.error) as e: wc = None if not wc: - summary['health'] = pygconst.Health.Critical; + summary['health'] = pygconst.Health.Critical summary['badreadings'].append( sdr.SensorReading({'name': 'HTTPS Service', 'states': ['Unreachable'], @@ -1848,7 +1866,8 @@ class XCCClient(IMMClient): # while usually the ipmi interrogation shall explain things, # just in case there is a gap, make sure at least the # health field is accurately updated - itemseverity = hmap.get(item.get('severity', 'E'), pygconst.Health.Critical) + itemseverity = hmap.get(item.get('severity', 'E'), + pygconst.Health.Critical) if (summary['health'] < itemseverity): summary['health'] = itemseverity if item['cmnid'] == 'FQXSPPW0104J': @@ -1896,7 +1915,8 @@ class XCCClient(IMMClient): if lic['status'] == 0: yield {'name': lic['feature'], 'state': 'Active'} elif lic['status'] == 10: - yield {'name': lic['feature'], 'state': 'Missing required license'} + yield {'name': lic['feature'], + 'state': 'Missing required license'} def save_licenses(self, directory): licdata = self.wc.grab_json_response('/api/providers/imm_fod') @@ -1938,8 +1958,8 @@ class XCCClient(IMMClient): rsp = json.loads(uploadthread.rsp) licpath = rsp.get('items', [{}])[0].get('path', None) if licpath: - rsp = self.wc.grab_json_response('/api/providers/imm_fod', - {'FOD_LicenseKeyInstall': licpath}) + rsp = self.wc.grab_json_response( + '/api/providers/imm_fod', {'FOD_LicenseKeyInstall': licpath}) if rsp.get('return', 0) in license_errors: raise pygexc.InvalidParameterValue( license_errors[rsp['return']]) diff --git a/pyghmi/ipmi/oem/lenovo/nextscale.py b/pyghmi/ipmi/oem/lenovo/nextscale.py index 9fa557ca..07d5a3a7 100644 --- a/pyghmi/ipmi/oem/lenovo/nextscale.py +++ b/pyghmi/ipmi/oem/lenovo/nextscale.py @@ -17,7 +17,7 @@ import pyghmi.constants as pygconst import pyghmi.exceptions as pygexc import pyghmi.ipmi.private.session as ipmisession -import pyghmi.ipmi.sdr as sdr +from pyghmi.ipmi import sdr import pyghmi.util.webclient as webclient import struct try: diff --git a/pyghmi/ipmi/sdr.py b/pyghmi/ipmi/sdr.py index 92536ecd..3ffe1e00 100644 --- a/pyghmi/ipmi/sdr.py +++ b/pyghmi/ipmi/sdr.py @@ -1,6 +1,3 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# coding=utf8 - # Copyright 2014 IBM Corporation # Copyright 2015 Lenovo # @@ -16,28 +13,35 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This module provides access to SDR offered by a BMC -# This data is common between 'sensors' and 'inventory' modules since SDR -# is both used to enumerate sensors for sensor commands and FRU ids for FRU -# commands +"""This module provides access to SDR offered by a BMC -# For now, we will not offer persistent SDR caching as we do in xCAT's IPMI -# code. Will see if it is adequate to advocate for high object reuse in a -# persistent process for the moment. +This data is common between 'sensors' and 'inventory' modules since SDR +is both used to enumerate sensors for sensor commands and FRU ids for FRU +commands -# Focus is at least initially on the aspects that make the most sense for a -# remote client to care about. For example, smbus information is being -# skipped for now +For now, we will not offer persistent SDR caching as we do in xCAT's IPMI +code. Will see if it is adequate to advocate for high object reuse in a +persistent process for the moment. + +Focus is at least initially on the aspects that make the most sense for a +remote client to care about. For example, smbus information is being +skipped for now +""" import math import os -import pyghmi.constants as const -import pyghmi.exceptions as exc -import pyghmi.ipmi.private.constants as ipmiconst import random import string import struct +import sys import weakref + +import pyghmi.constants as const +import pyghmi.exceptions as exc +import pyghmi.ipmi.command as ipmicmd +import pyghmi.ipmi.private.constants as ipmiconst +import six + try: import cPickle as pickle except ImportError: @@ -49,6 +53,7 @@ TYPE_FRU = 2 shared_sdrs = {} + def ones_complement(value, bits): # utility function to help with the large amount of 2s # complement prevalent in ipmi spec @@ -553,18 +558,15 @@ class SDREntry(object): elif linearization == 10: return math.sqrt(decoded) elif linearization == 11: - return decoded ** (1.0/3) + return decoded ** (1.0 / 3) else: raise NotImplementedError def decode_formula(self, entry): - self.m = \ - twos_complement(entry[0] + ((entry[1] & 0b11000000) << 2), 10) + self.m = twos_complement(entry[0] + ((entry[1] & 0b11000000) << 2), 10) self.tolerance = entry[1] & 0b111111 - self.b = \ - twos_complement(entry[2] + ((entry[3] & 0b11000000) << 2), 10) - self.accuracy = (entry[3] & 0b111111) + \ - (entry[4] & 0b11110000) << 2 + self.b = twos_complement(entry[2] + ((entry[3] & 0b11000000) << 2), 10) + self.accuracy = (entry[3] & 0b111111) + (entry[4] & 0b11110000) << 2 self.accuracyexp = (entry[4] & 0b1100) >> 2 self.direction = entry[4] & 0b11 # 0 = n/a, 1 = input, 2 = output @@ -580,7 +582,8 @@ class SDREntry(object): return "" if ipmitype == 0: # Unicode per 43.15 in ipmi 2.0 spec # the spec is not specific about encoding, assuming utf8 - return unicode(struct.pack("%dB" % len(data), *data), "utf_8") + return six.text_type(struct.pack("%dB" % len(data), *data), + "utf_8") elif ipmitype == 1: # BCD '+' tmpl = "%02X" * len(data) tstr = tmpl % tuple(data) @@ -811,10 +814,8 @@ class SDR(object): # decode information return "".join(hex(x) for x in auxdata) + if __name__ == "__main__": # test code - import os - import pyghmi.ipmi.command as ipmicmd - import sys password = os.environ['IPMIPASSWORD'] bmc = sys.argv[1] user = sys.argv[2] diff --git a/pyghmi/redfish/oem/lenovo/xcc.py b/pyghmi/redfish/oem/lenovo/xcc.py index 690c712b..45ace311 100644 --- a/pyghmi/redfish/oem/lenovo/xcc.py +++ b/pyghmi/redfish/oem/lenovo/xcc.py @@ -12,19 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pyghmi.redfish.oem.generic as generic -from pyghmi.util.parse import parse_time + import errno import json import math import os +import random import socket import time -import pyghmi.ipmi.private.util as util + import pyghmi.exceptions as pygexc +import pyghmi.ipmi.private.util as util +import pyghmi.redfish.oem.generic as generic import pyghmi.storage as storage +from pyghmi.util.parse import parse_time import pyghmi.util.webclient as webclient -import random class OEMHandler(generic.OEMHandler): @@ -50,7 +52,8 @@ class OEMHandler(generic.OEMHandler): return {'height': u_height, 'slot': slot} def _get_agentless_firmware(self, components): - adata = self.wc.grab_json_response('/api/dataset/imm_adapters?params=pci_GetAdapters') + adata = self.wc.grab_json_response( + '/api/dataset/imm_adapters?params=pci_GetAdapters') anames = set() for adata in adata.get('items', []): baseaname = adata['adapterName'] @@ -93,6 +96,7 @@ class OEMHandler(generic.OEMHandler): 'productName'].rstrip() bdata['version'] = diskent['fwVersion'] return (diskname, bdata) + def _get_disk_firmware(self, coponents): storagedata = storagedata = self.wc.grab_json_response( '/api/function/raid_alldevices?params=storage_GetAllDisks') @@ -300,7 +304,9 @@ class OEMHandler(generic.OEMHandler): 'perspan': drivesperspan, } else: - pass # TODO: adding new volume to existing array would be here + # TODO(Jarrod Johnson): adding new volume to + # existing array would be here + pass def _create_array(self, pool): params = self._parse_array_spec(pool) @@ -435,7 +441,8 @@ class OEMHandler(generic.OEMHandler): vminfo = self._do_web_request(vmurl, cache=False) if vminfo['ConnectedVia'] != 'NotConnected': continue - self._do_web_request(vmurl, {'Image': url, 'Inserted': True}, 'PATCH') + self._do_web_request(vmurl, {'Image': url, 'Inserted': True}, + 'PATCH') raise pygexc.BypassGenericBehavior() break else: @@ -485,7 +492,6 @@ class OEMHandler(generic.OEMHandler): raise Exception('Unrecognized return: ' + errmsg) if progress: progress({'phase': 'complete'}) - #self.weblogout() def update_firmware(self, filename, data=None, progress=None, bank=None): result = None @@ -501,15 +507,12 @@ class OEMHandler(generic.OEMHandler): self._refresh_token() self.wc.grab_json_response('/api/providers/fwupdate', json.dumps( {'UPD_WebCancel': 1})) - #self.weblogout() raise self.updating = False - #self.weblogout() return result def update_firmware_backend(self, filename, data=None, progress=None, bank=None): - #self.weblogout() self._refresh_token() rsv = self.wc.grab_json_response('/api/providers/fwupdate', json.dumps( {'UPD_WebReserve': 1})) @@ -530,7 +533,8 @@ class OEMHandler(generic.OEMHandler): progress({'phase': 'upload', 'progress': 100.0 * rsp['received'] / rsp['size']}) elif rsp['state'] != 'done': - if rsp.get('status', None) == 413 or uploadthread.rspstatus == 413: + if (rsp.get('status', None) == 413 or + uploadthread.rspstatus == 413): raise Exception('File is larger than supported') raise Exception('Unexpected result:' + repr(rsp)) uploadstate = rsp['state'] @@ -566,7 +570,8 @@ class OEMHandler(generic.OEMHandler): elif rsp.get('return', -1) == 108: raise Exception('Temporary error validating update, try again') elif rsp.get('return', -1) == 109: - raise Exception('Invalid update file or component does not support remote update') + raise Exception('Invalid update file or component ' + 'does not support remote update') elif rsp.get('return', -1) != 0: errmsg = repr(rsp) if rsp else self.wc.lastjsonerror raise Exception('Unexpected return to verify: ' + errmsg) @@ -577,12 +582,13 @@ class OEMHandler(generic.OEMHandler): rsp, status = self.wc.grab_json_response_with_status( '/api/providers/fwupdate', json.dumps({'UPD_WebVerifyUploadFileStatus': 1})) - if not rsp or status != 200 or rsp.get('return', -1) == 2: + if not rsp or status != 200 or rsp.get('return', -1) == 2: # The XCC firmware predates the FileStatus api verifyuploadfilersp = rsp break if rsp.get('return', -1) == 109: - raise Exception('Invalid update file or component does not support remote update') + raise Exception('Invalid update file or component ' + 'does not support remote update') if rsp.get('return', -1) != 0: errmsg = repr(rsp) if rsp else self.wc.lastjsonerror raise Exception( @@ -611,7 +617,7 @@ class OEMHandler(generic.OEMHandler): 'TDM', 'WINDOWS DRIV', 'LINUX DRIVER', 'UEFI', 'IMM'): # adapter firmware webid = rsp['items'][0]['webfile_build_id'] - locations = webid[webid.find('[')+1:webid.find(']')] + locations = webid[webid.find('[') + 1:webid.find(']')] locations = locations.split(':') validselectors = set([]) for loc in locations: @@ -721,7 +727,10 @@ class OEMHandler(generic.OEMHandler): if lic['status'] == 0: yield {'name': lic['feature'], 'state': 'Active'} if lic['status'] == 10: - yield {'name': lic['feature'], 'state': 'Missing required license'} + yield { + 'name': lic['feature'], + 'state': 'Missing required license' + } def delete_license(self, name): licdata = self.wc.grab_json_response('/api/providers/imm_fod') @@ -729,7 +738,11 @@ class OEMHandler(generic.OEMHandler): if lic.get('feature', None) == name: licid = ','.join((str(lic['type']), str(lic['id']))) self.wc.grab_json_response( - '/api/providers/imm_fod', {'FOD_LicenseKeyDelete': licid}) + '/api/providers/imm_fod', + { + 'FOD_LicenseKeyDelete': licid + } + ) break def save_licenses(self, directory): @@ -763,8 +776,12 @@ class OEMHandler(generic.OEMHandler): rsp = json.loads(uploadthread.rsp) licpath = rsp.get('items', [{}])[0].get('path', None) if licpath: - rsp = self.wc.grab_json_response('/api/providers/imm_fod', - {'FOD_LicenseKeyInstall': licpath}) + rsp = self.wc.grab_json_response( + '/api/providers/imm_fod', + { + 'FOD_LicenseKeyInstall': licpath + } + ) if rsp.get('return', 0) in license_errors: raise pygexc.InvalidParameterValue( license_errors[rsp['return']])