From d363fb382159b1ba9d858cebe05740132e699133 Mon Sep 17 00:00:00 2001 From: lijingxin Date: Thu, 22 Jun 2017 16:00:16 +0800 Subject: [PATCH] Pyghmi does not follow the pep8 standard Pep8 check is necessory for our code, it also makes your code easy to readable and understandable and beautiful. Change-Id: I4bb8434db07bf8fe82a4f65dfdfe7f2114b00083 Closes-Bug: #1699298 --- doc/source/conf.py | 79 +++---- pyghmi/ipmi/bmc.py | 4 +- pyghmi/ipmi/command.py | 26 +-- pyghmi/ipmi/console.py | 67 +++--- pyghmi/ipmi/fru.py | 6 +- pyghmi/ipmi/private/constants.py | 4 +- pyghmi/ipmi/private/serversession.py | 20 +- pyghmi/ipmi/private/session.py | 299 ++++++++++++++------------- pyghmi/ipmi/private/util.py | 4 +- pyghmi/ipmi/sdr.py | 45 ++-- pyghmi/util/webclient.py | 11 +- 11 files changed, 293 insertions(+), 272 deletions(-) diff --git a/doc/source/conf.py b/doc/source/conf.py index 0994cf2e..cdf8326b 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -15,6 +15,8 @@ import os import sys +from pyghmi.version import version_info + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -25,7 +27,7 @@ sys.path.insert(0, os.path.abspath('.')) # -- General configuration --------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -38,7 +40,7 @@ templates_path = ['_templates'] source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -51,7 +53,7 @@ copyright = u'2013, Jarrod Johnson ' # |version| and |release|, also used in various other places throughout the # built documents. # -from pyghmi.version import version_info + # The full version, including alpha/beta/rc tags. release = version_info.release_string() # The short X.Y version. @@ -59,37 +61,37 @@ version = version_info.version_string() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output ------------------------------------------------- @@ -101,26 +103,26 @@ html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -129,56 +131,55 @@ html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'pyghmidoc' - # -- Options for LaTeX output ------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]) @@ -190,26 +191,26 @@ latex_documents = [ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output ------------------------------------------- diff --git a/pyghmi/ipmi/bmc.py b/pyghmi/ipmi/bmc.py index 8cebd26a..4d6c23bd 100644 --- a/pyghmi/ipmi/bmc.py +++ b/pyghmi/ipmi/bmc.py @@ -14,13 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -__author__ = 'jjohnson2@lenovo.com' - import pyghmi.ipmi.command as ipmicommand import pyghmi.ipmi.private.serversession as serversession import pyghmi.ipmi.private.session as ipmisession import traceback +__author__ = 'jjohnson2@lenovo.com' + class Bmc(serversession.IpmiServer): def cold_reset(self): diff --git a/pyghmi/ipmi/command.py b/pyghmi/ipmi/command.py index 1cc86f5b..14ca1712 100644 --- a/pyghmi/ipmi/command.py +++ b/pyghmi/ipmi/command.py @@ -23,6 +23,7 @@ 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 + try: from pyghmi.ipmi.private import session except ImportError: @@ -42,7 +43,6 @@ try: except NameError: buffer = memoryview - boot_devices = { 'net': 4, 'network': 4, @@ -90,7 +90,7 @@ def _mask_to_cidr(mask): def _cidr_to_mask(prefix): - return struct.pack('>I', 2**prefix-1 << (32-prefix)) + return struct.pack('>I', 2 ** prefix - 1 << (32 - prefix)) class Command(object): @@ -435,7 +435,7 @@ class Command(object): response = self.raw_command(netfn=0, command=1) if 'error' in response: raise exc.IpmiException(response['error']) - assert(response['command'] == 1 and response['netfn'] == 1) + assert (response['command'] == 1 and response['netfn'] == 1) powerstate = 'on' if (response['data'][0] & 1) else 'off' return {'powerstate': powerstate} @@ -763,7 +763,7 @@ class Command(object): 1: 'Static', 2: 'DHCP', 3: 'BIOS', - 4: 'Other', + 4: 'Other', } retdata['ipv4_configuration'] = v4cfgmethods[self._fetch_lancfg_param( channel, 4)] @@ -825,7 +825,7 @@ class Command(object): that """ if self._netchannel is None: - for channel in chain((0xe, ), range(1, 0xc)): + for channel in chain((0xe,), range(1, 0xc)): try: rsp = self.xraw_command( netfn=6, command=0x42, data=(channel,)) @@ -1105,7 +1105,7 @@ class Command(object): return retstr def _chunkwise_dcmi_set(self, command, data): - chunks = [data[i:i+15] for i in range(0, len(data), 15)] + chunks = [data[i:i + 15] for i in range(0, len(data), 15)] offset = 0 for chunk in chunks: chunk = bytearray(chunk, 'utf-8') @@ -1184,7 +1184,7 @@ class Command(object): 'dont_change': 0, 'non_volatile': 1, 'volatile': 2, - #'reserved': 3 + # 'reserved': 3 } b = 0 b |= (access_update_modes[access_update_mode] << 6) & 0b11000000 @@ -1207,7 +1207,7 @@ class Command(object): 'dont_change': 0, 'non_volatile': 1, 'volatile': 2, - #'reserved': 3 + # 'reserved': 3 } b |= (privilege_update_modes[privilege_update_mode] << 6) & 0b11000000 privilege_levels = { @@ -1292,7 +1292,7 @@ class Command(object): 3: 'operator', 4: 'administrator', 5: 'proprietary', - #0x0F: 'no_access' + # 0x0F: 'no_access' } r['privilege_level'] = privilege_levels[data[1] & 0b00001111] return r @@ -1337,8 +1337,8 @@ class Command(object): 0x0a: 'reserved for USB 1.x', 0x0b: 'reserved for USB 2.x', 0x0c: 'System Interface (KCS, SMIC, or BT)', - ## 60h-7Fh: OEM - ## all other reserved + # 60h-7Fh: OEM + # all other reserved } t = data[1] & 0b01111111 if t in channel_medium_types: @@ -1450,7 +1450,7 @@ class Command(object): privilege_level: [reserved, callback, user, operatorm administrator, proprietary, no_access] """ - ## user access available during call-in or callback direct connection + # user access available during call-in or callback direct connection if channel is None: channel = self.get_network_channel() data = [channel, uid] @@ -1636,7 +1636,7 @@ class Command(object): channel = self.get_network_channel() names = {} max_ids = self.get_channel_max_user_count(channel) - for uid in range(1, max_ids+1): + for uid in range(1, max_ids + 1): name = self.get_user_name(uid=uid) if name is not None: names[uid] = self.get_user(uid=uid, channel=channel) diff --git a/pyghmi/ipmi/console.py b/pyghmi/ipmi/console.py index 95e85515..90ce2c88 100644 --- a/pyghmi/ipmi/console.py +++ b/pyghmi/ipmi/console.py @@ -38,7 +38,7 @@ class Console(object): :param kg: optional parameter for BMCs configured to require it """ - #TODO(jbjohnso): still need an exit and a data callin function + # TODO(jbjohnso): still need an exit and a data callin function def __init__(self, bmc, userid, password, iohandler, port=623, force=False, kg=None): @@ -70,19 +70,19 @@ class Console(object): if 'error' in response: self._print_error(response['error']) return - #Send activate sol payload directive - #netfn= 6 (application) - #command = 0x48 (activate payload) - #data = (1, sol payload type + # Send activate sol payload directive + # netfn= 6 (application) + # command = 0x48 (activate payload) + # data = (1, sol payload type # 1, first instance # 0b11000000, -encrypt, authenticate, # disable serial/modem alerts, CTS fine # 0, 0, 0 reserved response = self.ipmi_session.raw_command(netfn=0x6, command=0x48, data=(1, 1, 192, 0, 0, 0)) - #given that these are specific to the command, - #it's probably best if one can grep the error - #here instead of in constants + # given that these are specific to the command, + # it's probably best if one can grep the error + # here instead of in constants sol_activate_codes = { 0x81: 'SOL is disabled', 0x82: 'Maximum SOL session count reached', @@ -118,19 +118,19 @@ class Console(object): self._print_error(response['error']) return self.activated = True - #data[0:3] is reserved except for the test mode, which we don't use + # data[0:3] is reserved except for the test mode, which we don't use data = response['data'] self.maxoutcount = (data[5] << 8) + data[4] - #BMC tells us this is the maximum allowed size - #data[6:7] is the promise of how small packets are going to be, but we - #don't have any reason to worry about it + # BMC tells us this is the maximum allowed size + # data[6:7] is the promise of how small packets are going to be, but we + # don't have any reason to worry about it if (data[8] + (data[9] << 8)) not in (623, 28418): - #TODO(jbjohnso): support atypical SOL port number + # TODO(jbjohnso): support atypical SOL port number raise NotImplementedError("Non-standard SOL Port Number") - #ignore data[10:11] for now, the vlan detail, shouldn't matter to this - #code anyway... - #NOTE(jbjohnso): - #We will use a special purpose keepalive + # ignore data[10:11] for now, the vlan detail, shouldn't matter to this + # code anyway... + # NOTE(jbjohnso): + # We will use a special purpose keepalive if self.ipmi_session.sol_handler is not None: # If there is erroneously another SOL handler already, notify # it of newly established session @@ -296,9 +296,9 @@ class Console(object): def _got_sol_payload(self, payload): """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 + # 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 if type(payload) == dict: # we received an error condition self.activated = False self._print_error(payload) @@ -310,15 +310,16 @@ class Console(object): poweredoff = payload[3] & 0b100000 deactivated = payload[3] & 0b10000 breakdetected = payload[3] & 0b100 - #for now, ignore overrun. I assume partial NACK for this reason or for - #no reason would be treated the same, new payload with partial data + # for now, ignore overrun. I assume partial NACK for this reason or + # for no reason would be treated the same, new payload with partial + # data. remdata = "" remdatalen = 0 if newseq != 0: # this packet at least has some data to send to us.. if len(payload) > 4: remdatalen = len(payload[4:]) # store remote len before dupe - #retry logic, we must ack *this* many even if it is - #a retry packet with new partial data + # retry logic, we must ack *this* many even if it is + # a retry packet with new partial data remdata = struct.pack("%dB" % remdatalen, *payload[4:]) if newseq == self.remseq: # it is a retry, but could have new data if remdatalen > self.lastsize: @@ -331,16 +332,16 @@ class Console(object): if remdata: # Do not subject callers to empty data self._print_data(remdata) ackpayload = (0, self.remseq, remdatalen, 0) - #Why not put pending data into the ack? because it's rare - #and might be hard to decide what to do in the context of - #retry situation + # Why not put pending data into the ack? because it's rare + # and might be hard to decide what to do in the context of + # retry situation try: self.send_payload(ackpayload, retry=False) except exc.IpmiException: - #if the session is broken, then close the SOL session + # if the session is broken, then close the SOL session self.close() if self.myseq != 0 and ackseq == self.myseq: # the bmc has something - # to say about last xmit + # to say about last xmit self.awaitingack = False if nacked and not breakdetected: # the BMC was in some way unhappy if poweredoff: @@ -379,9 +380,9 @@ class Console(object): to provide their own event loop behavior, though this could be used within the greenthread implementation of caller's choice if desired. """ - #wait_for_rsp promises to return a false value when no sessions are - #alive anymore - #TODO(jbjohnso): wait_for_rsp is not returning a true value for our own - #session + # wait_for_rsp promises to return a false value when no sessions are + # alive anymore + # TODO(jbjohnso): wait_for_rsp is not returning a true value for our + # own session while (1): session.Session.wait_for_rsp(timeout=600) diff --git a/pyghmi/ipmi/fru.py b/pyghmi/ipmi/fru.py index cbf2d8ca..6b3bd243 100644 --- a/pyghmi/ipmi/fru.py +++ b/pyghmi/ipmi/fru.py @@ -187,8 +187,8 @@ class FRU(object): frusubtype = self.sdr.fru_type_and_modifier & 0xff if frutype > 0x10 or frutype < 0x8 or frusubtype not in (0, 1, 2): return - #TODO(jjohnson2): strict mode to detect pyghmi and BMC - #gaps + # TODO(jjohnson2): strict mode to detect pyghmi and BMC + # gaps # raise iexc.PyghmiException( # 'Unsupported FRU device: {0:x}h, {1:x}h'.format(frutype, # frusubtype @@ -199,7 +199,7 @@ class FRU(object): return if self.databytes[0] != 1: return - #TODO(jjohnson2): strict mode to flag potential BMC errors + # TODO(jjohnson2): strict mode to flag potential BMC errors # raise iexc.BmcErrorException("Invalid/Unsupported FRU format") # Ignore the internal use even if present. self._parse_chassis() diff --git a/pyghmi/ipmi/private/constants.py b/pyghmi/ipmi/private/constants.py index 84a5633e..193dba0c 100644 --- a/pyghmi/ipmi/private/constants.py +++ b/pyghmi/ipmi/private/constants.py @@ -14,11 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +import pyghmi.constants as const + IPMI_BMC_ADDRESS = 0x20 IPMI_SEND_MESSAGE_CMD = 0x34 -import pyghmi.constants as const - payload_types = { 'ipmi': 0x0, diff --git a/pyghmi/ipmi/private/serversession.py b/pyghmi/ipmi/private/serversession.py index 55632d6b..dbbe1430 100644 --- a/pyghmi/ipmi/private/serversession.py +++ b/pyghmi/ipmi/private/serversession.py @@ -42,11 +42,11 @@ class ServerSession(ipmisession.Session): # role = request[1] self.clientsessionid = request[4:8] # TODO(jbjohnso): intelligently handle integrity/auth/conf - #for now, forcibly do cipher suite 3 + # for now, forcibly do cipher suite 3 self.managedsessionid = os.urandom(4) - #table 13-17, 1 for now (hmac-sha1), 3 should also be supported - #table 13-18, integrity, 1 for now is hmac-sha1-96, 4 is sha256 - #confidentiality: 1 is aes-cbc-128, the only one + # table 13-17, 1 for now (hmac-sha1), 3 should also be supported + # table 13-18, integrity, 1 for now is hmac-sha1-96, 4 is sha256 + # confidentiality: 1 is aes-cbc-128, the only one self.privlevel = 4 response = (bytearray([clienttag, 0, self.privlevel, 0]) + self.clientsessionid + self.managedsessionid + @@ -97,7 +97,7 @@ class ServerSession(ipmisession.Session): self.maxpriv = self.rolem & 0b111 namepresent = data[27] if namepresent == 0: - #ignore null username for now + # ignore null username for now return self.username = bytes(data[28:]) if self.username.decode('utf-8') not in self.authdata: @@ -152,7 +152,7 @@ class ServerSession(ipmisession.Session): ).digest() authcode = struct.pack("%dB" % len(data[8:]), *data[8:]) if expectedauthcode != authcode: - #TODO(jjohnson2): RMCP error back at invalid rakp3 + # TODO(jjohnson2): RMCP error back at invalid rakp3 return clienttag = data[0] if data[1] != 0: @@ -207,7 +207,7 @@ class ServerSession(ipmisession.Session): After the session inactivity timeout, this invalidate the client session. """ - #for now, we will have a non-configurable 60 second timeout + # for now, we will have a non-configurable 60 second timeout pass def _handle_channel_auth_cap(self, request): @@ -226,9 +226,9 @@ class ServerSession(ipmisession.Session): class IpmiServer(object): - #auth capabilities for now is a static payload - #for now always completion code 0, otherwise ignore - #authentication type fixed to ipmi2, ipmi1 forbidden + # auth capabilities for now is a static payload + # for now always completion code 0, otherwise ignore + # authentication type fixed to ipmi2, ipmi1 forbidden # 0b10000000 def __init__(self, authdata, port=623, bmcuuid=None, address='::'): diff --git a/pyghmi/ipmi/private/session.py b/pyghmi/ipmi/private/session.py index e67437f6..e9d3e5ad 100644 --- a/pyghmi/ipmi/private/session.py +++ b/pyghmi/ipmi/private/session.py @@ -42,31 +42,37 @@ except AttributeError: def dictitems(d): return d.items() - -initialtimeout = 0.5 # minimum timeout for first packet to retry in any given - # session. This will be randomized to stagger out retries - # in case of congestion -iothread = None # the thread in which all IO will be performed - # While the model as-is works fine for it's own coroutine - # structure, when combined with threading or something like - # eventlet, it becomes difficult for the calling code to cope - # This thread will tuck away the threading situation such that - # calling code doesn't have to do any gymnastics to cope with - # the nature of things. -iothreadready = False # whether io thread is yet ready to work -iothreadwaiters = [] # threads waiting for iothreadready +# minimum timeout for first packet to retry in any given +# session. This will be randomized to stagger out retries +# in case of congestion +initialtimeout = 0.5 +# the thread in which all IO will be performed +# While the model as-is works fine for it's own coroutine +# structure, when combined with threading or something like +# eventlet, it becomes difficult for the calling code to cope +# This thread will tuck away the threading situation such that +# calling code doesn't have to do any gymnastics to cope with +# the nature of things. +iothread = None +# whether io thread is yet ready to work +iothreadready = False +# threads waiting for iothreadready +iothreadwaiters = [] ioqueue = collections.deque([]) myself = None ipv6support = None selectdeadline = 0 running = True -iosockets = [] # set of iosockets that will be shared amongst Session objects -MAX_BMCS_PER_SOCKET = 64 # no more than this many BMCs will share a socket - # this could be adjusted based on rmem_max - # value, leading to fewer filehandles +# set of iosockets that will be shared amongst Session objects +iosockets = [] +# no more than this many BMCs will share a socket +# this could be adjusted based on rmem_max +# value, leading to fewer filehandles +MAX_BMCS_PER_SOCKET = 64 -MAX_IDLE = 29 # maximum time to allow idle, more than this and BMC may assume - # incorrect idle +# maximum time to allow idle, more than this and BMC may assume +MAX_IDLE = 29 +# incorrect idle def define_worker(): @@ -134,6 +140,7 @@ def define_worker(): directediowaiters[workitem[2]].append(workitem) else: directediowaiters[workitem[2]] = [workitem] + return _IOWorker @@ -160,7 +167,7 @@ def _io_wait(timeout, myaddr=None, evq=None): def _io_sendto(mysocket, packet, sockaddr): - #Want sendto to act reasonably sane.. + # Want sendto to act reasonably sane.. mysocket.setblocking(1) if hasattr(mysocket, 'fd'): mysocket = mysocket.fd @@ -235,7 +242,7 @@ def _aespad(data): currlen = len(data) + 1 # need to count the pad length field as well neededpad = currlen % 16 if neededpad: # if it happens to be zero, hurray, but otherwise invert the - # sense of the padding + # sense of the padding neededpad = 16 - neededpad padval = 1 while padval <= neededpad: @@ -279,11 +286,11 @@ class Session(object): keepalive_sessions = {} peeraddr_to_nodes = {} iterwaiters = [] - #NOTE(jbjohnso): - #socketpool is a mapping of sockets to usage count + # NOTE(jbjohnso): + # socketpool is a mapping of sockets to usage count socketpool = {} - #this will be a lock. Delay the assignment so that a calling framework - #can do something like reassign our threading and select modules + # this will be a lock. Delay the assignment so that a calling framework + # can do something like reassign our threading and select modules socketchecking = None @classmethod @@ -436,8 +443,8 @@ class Session(object): self.cleaningup = False self.lastpayload = None self._customkeepalives = None - self.evq = collections.deque([]) # queue of events denoting line to - # run a cmd + # queue of events denoting line to run a cmd + self.evq = collections.deque([]) self.bmc = bmc self.broken = False # a private queue for packets for which this session handler @@ -492,11 +499,11 @@ class Session(object): self.logging = False if self._customkeepalives: for ka in list(self._customkeepalives): - # Be thorough and notify parties through their custom - # keepalives. In practice, this *should* be the same, but - # if a code somehow makes duplicate SOL handlers, - # this would notify all the handlers rather than just the - # last one to take ownership + # Be thorough and notify parties through their custom + # keepalives. In practice, this *should* be the same, but + # if a code somehow makes duplicate SOL handlers, + # this would notify all the handlers rather than just the + # last one to take ownership self._customkeepalives[ka][1]( {'error': 'Session Disconnected'}) self._customkeepalives = None @@ -568,10 +575,10 @@ class Session(object): head = [constants.IPMI_BMC_ADDRESS, constants.netfn_codes['application'] << 2] check_sum = _checksum(*head) - #NOTE(fengqian): according IPMI Figure 14-11, rqSWID is set to 81h + # NOTE(fengqian): according IPMI Figure 14-11, rqSWID is set to 81h boday = [0x81, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD, 0x40 | channel] - #NOTE(fengqian): Track request + # NOTE(fengqian): Track request self._add_request_entry((constants.netfn_codes['application'] + 1, self.seqlun, constants.IPMI_SEND_MESSAGE_CMD)) return head + [check_sum] + boday @@ -597,17 +604,17 @@ class Session(object): """ bridge_msg = [] self.expectedcmd = command - self.expectednetfn = netfn + \ - 1 # in ipmi, the response netfn is always one - # higher than the request payload, we assume - # we are always the requestor for now + # in ipmi, the response netfn is always one + self.expectednetfn = netfn + 1 + # higher than the request payload, we assume + # we are always the requestor for now seqincrement = 7 # IPMI spec forbids gaps bigger then 7 in seq number. - # Risk the taboo rather than violate the rules + # Risk the taboo rather than violate the rules while (not self.servermode and (netfn, command, self.seqlun) in self.tabooseq and self.tabooseq[(netfn, command, self.seqlun)] and seqincrement): self.tabooseq[(self.expectednetfn, command, self.seqlun)] -= 1 - # Allow taboo to eventually expire after a few rounds + # Allow taboo to eventually expire after a few rounds self.seqlun += 4 # the last two bits are lun, so add 4 to add 1 self.seqlun &= 0xff # we only have one byte, wrap when exceeded seqincrement -= 1 @@ -616,7 +623,7 @@ class Session(object): addr = bridge_request.get('addr', 0x0) channel = bridge_request.get('channel', 0x0) bridge_msg = self._make_bridge_request_msg(channel, netfn, command) - #NOTE(fengqian): For bridge request, rsaddr is specified and + # NOTE(fengqian): For bridge request, rsaddr is specified and # rqaddr is BMC address. rqaddr = constants.IPMI_BMC_ADDRESS rsaddr = addr @@ -625,7 +632,7 @@ class Session(object): rsaddr = constants.IPMI_BMC_ADDRESS if self.servermode: rsaddr = self.clientaddr - #figure 13-4, first two bytes are rsaddr and + # figure 13-4, first two bytes are rsaddr and # netfn, for non-bridge request, rsaddr is always 0x20 since we are # addressing BMC while rsaddr is specified forbridge request header = [rsaddr, netfn << 2] @@ -636,7 +643,7 @@ class Session(object): payload = header + [headsum] + reqbody + [bodysum] if bridge_request: payload = bridge_msg + payload - #NOTE(fengqian): For bridge request, another check sum is needed. + # NOTE(fengqian): For bridge request, another check sum is needed. tail_csum = _checksum(*payload[3:]) payload.append(tail_csum) @@ -714,12 +721,12 @@ class Session(object): timeout = None else: # if not retry, give it a second before surrending timeout = 1 - #The event loop is shared amongst pyghmi session instances - #within a process. In this way, synchronous usage of the interface - #plays well with asynchronous use. In fact, this produces the behavior - #of only the constructor needing a callback. From then on, - #synchronous usage of the class acts in a greenthread style governed by - #order of data on the network + # The event loop is shared amongst pyghmi session instances + # within a process. In this way, synchronous usage of the interface + # plays well with asynchronous use. In fact, this produces the + # behavior of only the constructor needing a callback. From then on, + # synchronous usage of the class acts in a greenthread style governed + # by order of data on the network self.awaitresponse(retry, waitall) lastresponse = self.lastresponse self.incommand = False @@ -759,12 +766,12 @@ class Session(object): :param timeout: Specify a custom timeout for long-running request """ if payload and self.lastpayload: - # we already have a packet outgoing, make this - # a pending payload - # this way a simplistic BMC won't get confused - # and we also avoid having to do more complicated - # retry mechanism where each payload is - # retried separately + # we already have a packet outgoing, make this + # a pending payload + # this way a simplistic BMC won't get confused + # and we also avoid having to do more complicated + # retry mechanism where each payload is + # retried separately self.pendingpayloads.append((payload, payload_type, retry)) return if payload_type is None: @@ -784,7 +791,7 @@ class Session(object): if self.ipmiversion == 2.0: message.append(payload_type) if baretype == 2: - #TODO(jbjohnso): OEM payload types + # TODO(jbjohnso): OEM payload types raise NotImplementedError("OEM Payloads") elif baretype not in constants.payload_types.values(): raise NotImplementedError( @@ -797,23 +804,22 @@ class Session(object): message += self._ipmi15authcode(payload) message.append(len(payload)) message += payload - totlen = 34 + \ - len(message) # Guessing the ipmi spec means the whole - # packet and assume no tag in old 1.5 world + # Guessing the ipmi spec means the whole + totlen = 34 + len(message) + # packet and assume no tag in old 1.5 world if totlen in (56, 84, 112, 128, 156): message.append(0) # Legacy pad as mandated by ipmi spec elif self.ipmiversion == 2.0: psize = len(payload) if self.confalgo: - pad = ( - psize + 1) % 16 # pad has to cope with one byte field like - # the _aespad function + pad = (psize + 1) % 16 # pad has to cope with one byte + # field like the _aespad function if pad: # if no pad needed, then we take no more action pad = 16 - pad - newpsize = psize + pad + \ - 17 # new payload size grew according to pad - # size, plus pad length, plus 16 byte IV - #(Table 13-20) + # new payload size grew according to pad + newpsize = psize + pad + 17 + # size, plus pad length, plus 16 byte IV + # (Table 13-20) message.append(newpsize & 0xff) message.append(newpsize >> 8) iv = os.urandom(16) @@ -830,26 +836,26 @@ class Session(object): message.append(psize >> 8) message += list(payload) if self.integrityalgo: # see table 13-8, - # RMCP+ packet format - # TODO(jbjohnso): SHA256 which is now - # allowed + # RMCP+ packet format + # TODO(jbjohnso): SHA256 which is now + # allowed neededpad = (len(message) - 2) % 4 if neededpad: neededpad = 4 - neededpad message += [0xff] * neededpad message.append(neededpad) message.append(7) # reserved, 7 is the required value for the - # specification followed + # specification followed integdata = message[4:] authcode = hmac.new(self.k1, struct.pack("%dB" % len(integdata), *integdata), hashlib.sha1).digest()[:12] # SHA1-96 - # per RFC2404 truncates to 96 bits + # per RFC2404 truncates to 96 bits message += struct.unpack("12B", authcode) self.netpacket = struct.pack("!%dB" % len(message), *message) - #advance idle timer since we don't need keepalive while sending packets - #out naturally + # advance idle timer since we don't need keepalive while sending + # packets out naturally if (self in Session.keepalive_sessions and not needskeepalive and not self._customkeepalives): Session.keepalive_sessions[self]['timeout'] = _monotonic_time() + \ @@ -857,10 +863,11 @@ class Session(object): self._xmit_packet(retry, delay_xmit=delay_xmit, timeout=timeout) def _ipmi15authcode(self, payload, checkremotecode=False): - #checkremotecode is used to verify remote code, - #otherwise this function is used to general authcode for local - if self.authtype == 0: # Only for things before auth in ipmi 1.5, not - # like 2.0 cipher suite 0 + # checkremotecode is used to verify remote code, + # otherwise this function is used to general authcode for local + if self.authtype == 0: + # Only for things before auth in ipmi 1.5, not + # like 2.0 cipher suite 0 return () password = self.password padneeded = 16 - len(password) @@ -920,13 +927,14 @@ class Session(object): self.sessionid = struct.unpack("> 2 - del payload[0:5] # ^^ remove header of rsaddr/netfn/lun/checksum/rq/seq/lun - del payload[-1] # remove the trailing checksum + del payload[0:5] + # remove the trailing checksum + del payload[-1] response['command'] = payload[0] if self.servermode: del payload[0:1] @@ -1593,21 +1608,21 @@ class Session(object): self.lastpayload = None self._relog() else: # in IPMI case, the only recourse is to act as if the packet is - # idempotent. SOL has more sophisticated retry handling - # the biggest risks are reset sp which is often fruitless to retry - # and chassis reset, which sometimes will shoot itself - # systematically in the head in a shared port case making replies - # impossible + # idempotent. SOL has more sophisticated retry handling + # the biggest risks are reset sp which is often fruitless to retry + # and chassis reset, which sometimes will shoot itself + # systematically in the head in a shared port case making replies + # impossible self.hasretried = 1 # remember so that we can track taboo - # combinations - # of sequence number, netfn, and lun due to - # ambiguity on the wire + # combinations + # of sequence number, netfn, and lun due to + # ambiguity on the wire self.send_payload() self.nowait = False def _xmit_packet(self, retry=True, delay_xmit=None, timeout=None): if self.sequencenumber: # seq number of zero will be left alone, it is - # special, otherwise increment + # special, otherwise increment self.sequencenumber += 1 if delay_xmit is not None: Session.waiting_sessions[self] = {} @@ -1617,8 +1632,9 @@ class Session(object): return # skip transmit, let retry timer do it's thing if self.sockaddr: _io_sendto(self.socket, self.netpacket, self.sockaddr) - else: # he have not yet picked a working sockaddr for this connection, - # try all the candidates that getaddrinfo provides + else: + # he have not yet picked a working sockaddr for this connection, + # try all the candidates that getaddrinfo provides self.allsockaddrs = [] try: for res in socket.getaddrinfo(self.bmc, @@ -1667,6 +1683,7 @@ class Session(object): if __name__ == "__main__": import sys + ipmis = Session(bmc=sys.argv[1], userid=sys.argv[2], password=os.environ['IPMIPASS']) diff --git a/pyghmi/ipmi/private/util.py b/pyghmi/ipmi/private/util.py index 325bf214..ab4bc62f 100644 --- a/pyghmi/ipmi/private/util.py +++ b/pyghmi/ipmi/private/util.py @@ -84,8 +84,8 @@ def get_ipmi_error(response, suffix=""): return False command = response['command'] netfn = response['netfn'] - if ((netfn, command) in constants.command_completion_codes - and code in constants.command_completion_codes[(netfn, command)]): + if ((netfn, command) in constants.command_completion_codes and + code in constants.command_completion_codes[(netfn, command)]): res = constants.command_completion_codes[(netfn, command)][code] res += suffix elif code in constants.ipmi_completion_codes: diff --git a/pyghmi/ipmi/sdr.py b/pyghmi/ipmi/sdr.py index 3d3ad171..7e9869aa 100644 --- a/pyghmi/ipmi/sdr.py +++ b/pyghmi/ipmi/sdr.py @@ -46,8 +46,8 @@ def ones_complement(value, bits): # complement prevalent in ipmi spec signbit = 0b1 << (bits - 1) if value & signbit: - #if negative, subtract 1, then take 1s - #complement given bits width + # if negative, subtract 1, then take 1s + # complement given bits width return 0 - (value ^ ((0b1 << bits) - 1)) else: return value @@ -58,8 +58,8 @@ def twos_complement(value, bits): # complement prevalent in ipmi spec signbit = 0b1 << (bits - 1) if value & signbit: - #if negative, subtract 1, then take 1s - #complement given bits width + # if negative, subtract 1, then take 1s + # complement given bits width return 0 - ((value - 1) ^ ((0b1 << bits) - 1)) else: return value @@ -261,7 +261,7 @@ class SDREntry(object): raise NotImplementedError self.rectype = entrybytes[3] self.linearization = None - #most important to get going are 1, 2, and 11 + # most important to get going are 1, 2, and 11 self.sdrtype = TYPE_SENSOR # assume a sensor if self.rectype == 1: # full sdr self.full_decode(entrybytes[5:]) @@ -312,7 +312,7 @@ class SDREntry(object): def association_decode(self, entry): # table 43-4 Entity Associaition Record - #TODO(jbjohnso): actually represent this data + # TODO(jbjohnso): actually represent this data self.sdrtype = TYPE_UNKNOWN def compact_decode(self, entry): @@ -337,10 +337,10 @@ class SDREntry(object): except KeyError: self.sensor_type = "UNKNOWN type " + str(entry[7]) self.reading_type = entry[8] # table 42-1 - # 0: unspecified - # 1: generic threshold based - # 0x6f: discrete sensor-specific from table 42-3, sensor offsets - # all others per table 42-2, generic discrete + # 0: unspecified + # 1: generic threshold based + # 0x6f: discrete sensor-specific from table 42-3, sensor offsets + # all others per table 42-2, generic discrete # numeric format is one of: # 0 - unsigned, 1 - 1s complement, 2 - 2s complement, 3 - ignore number # compact records are supposed to always write it as '3', presumably @@ -371,8 +371,8 @@ class SDREntry(object): self.modunit def full_decode(self, entry): - #offsets are table from spec, minus 6 - #TODO(jbjohnso): table 43-13, put in constants to interpret entry[3] + # offsets are table from spec, minus 6 + # TODO(jbjohnso): table 43-13, put in constants to interpret entry[3] self._common_decode(entry) # now must extract the formula data to transform values # entry[18 to entry[24]. @@ -543,7 +543,7 @@ class SDREntry(object): (entry[4] & 0b11110000) << 2 self.accuracyexp = (entry[4] & 0b1100) >> 2 self.direction = entry[4] & 0b11 - #0 = n/a, 1 = input, 2 = output + # 0 = n/a, 1 = input, 2 = output self.resultexponent = twos_complement((entry[5] & 0b11110000) >> 4, 4) bexponent = twos_complement(entry[5] & 0b1111, 4) # might as well do the math to 'b' now rather than wait for later @@ -594,7 +594,7 @@ class SDR(object): self.read_info() def read_info(self): - #first, we want to know the device id + # first, we want to know the device id rsp = self.ipmicmd.xraw_command(netfn=6, command=1) rsp['data'] = bytearray(rsp['data']) self.device_id = rsp['data'][0] @@ -618,9 +618,10 @@ class SDR(object): # be able to proceed # However at the moment, we haven't done so raise NotImplementedError - return # We have Device SDR, without SDR Repository device, but - # also without sensor device support, no idea how to - # continue + return + # We have Device SDR, without SDR Repository device, but + # also without sensor device support, no idea how to + # continue self.get_sdr() def get_sdr_reservation(self): @@ -636,14 +637,14 @@ class SDR(object): # we only understand SDR version 51h, the only version defined # at time of this writing raise NotImplementedError - #NOTE(jbjohnso): we actually don't need to care about 'numrecords' + # NOTE(jbjohnso): we actually don't need to care about 'numrecords' # since FFFF marks the end explicitly - #numrecords = (rsp['data'][2] << 8) + rsp['data'][1] - #NOTE(jbjohnso): don't care about 'free space' at the moment - #NOTE(jbjohnso): most recent timstamp data for add and erase could be + # numrecords = (rsp['data'][2] << 8) + rsp['data'][1] + # NOTE(jbjohnso): don't care about 'free space' at the moment + # NOTE(jbjohnso): most recent timstamp data for add and erase could be # handy to detect cache staleness, but for now will assume invariant # over life of session - #NOTE(jbjohnso): not looking to support the various options in op + # NOTE(jbjohnso): not looking to support the various options in op # support, ignore those for now, reservation if some BMCs can't read # full SDR in one slurp recid = 0 diff --git a/pyghmi/util/webclient.py b/pyghmi/util/webclient.py index 26bb4588..ce9bedec 100644 --- a/pyghmi/util/webclient.py +++ b/pyghmi/util/webclient.py @@ -16,7 +16,10 @@ # sake of typical internal management devices. Compatibility back to python # 2.6 as is found in commonly used enterprise linux distributions. -__author__ = 'jjohnson2' +import json +import pyghmi.exceptions as pygexc +import socket +import ssl try: import Cookie @@ -24,10 +27,8 @@ try: except ImportError: import http.client as httplib import http.cookies as Cookie -import json -import pyghmi.exceptions as pygexc -import socket -import ssl + +__author__ = 'jjohnson2' class SecureHTTPConnection(httplib.HTTPConnection, object):